[CF 321D]Ciel and Flipboard解題報告

題意

有一個n*n矩陣aij。n爲奇數,m=(n+1)/2。我們每次可以選中一個m*m的子矩陣,將其中所有元素乘以-1.求最後矩陣中所有元素的最大和。n<=33.

分析

這道題是個結論題。

對於第i行的三個值:j、m、m+j,每個子矩陣要麼覆蓋其中的0個要麼覆蓋其中2個。

例如:n=5,m=3,那麼對於某行的第1、3、4這三個元素,子矩陣要麼覆蓋其中0個要麼覆蓋其中2個。對於2、3、5也是如此。

顯然對於列也有類似結論。

如果我們把正反視作01(一開始均爲0,乘以-1就異或1),設(i,j)的翻轉狀態爲setup[i][j],那麼setup[i][j]^setup[i][m]^setup[i][m+j]=0.當然這裏要求k<m。類似地,setup[i][j]^setup[m][j]^setup[i+m][j]=0.於是我們只要知道其中兩個,就能求出來第三個。

而且,只要setup矩陣滿足這一條件,就一定能用一系列操作變換出來這個setup矩陣。

證法其實挺巧的:將每個a[i][j]都視作一維,那麼每個m*m的子矩陣都可以視作n*n維線性空間中的向量。所有這些向量都是線性無關的(直觀上看,不可能用若干個子矩陣操作將某一個別的子矩陣操作抵消掉)。而一共有m*m種不同的操作,因此最終一定有2^(m*m)個不同的setup矩陣。另一方面,只要確定了左上角的m*m個數,那整個setup矩陣亦隨之確定,總數也是2^(m*m)種。所以每個滿足條件的setup矩陣一定能變換出來。

因此我們可以O(2^m)枚舉setup矩陣第m列的前m個數。確定它們之後,第m列的後n-m個數亦隨之確定。然後可以發現,對於j<m,每個setup[m][j]都是獨立的。那我們可以分別求出setup[m][j]取0、1時對答案的最大貢獻:若setup[m][j]確定,則setup[m][j+k]確定,且對於每個i<m,當setup[i][j]確定時,setup[i][j+m]、setup[i+m][j]、setup[i+m][j+m]均唯一確定,枚舉之即可。

總複雜度O(m*2^m)。

代碼

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int INF=0x7fffffff/2;
const int SIZEN=50;
int N,M;
int A[SIZEN][SIZEN];
int setup[SIZEN][SIZEN];
int sgn(int a){//0是未翻,1是翻了
	return !a?1:-1;
}
int single_val(int x,int y){
	return sgn(setup[x][y])*A[x][y];
}
int calc_unit(int x,int y,int d){//(x,y)的四元組,要求中線均已放在setup中
	int ans=0;
	setup[x][y]=d;
	ans+=single_val(x,y);
	setup[x][y+M]=setup[x][y]^setup[x][M];
	ans+=single_val(x,y+M);
	setup[x+M][y]=setup[x][y]^setup[M][y];
	ans+=single_val(x+M,y);
	setup[x+M][y+M]=setup[x+M][y]^setup[x+M][M];
	ans+=single_val(x+M,y+M);
	return ans;
}
int calc_unit(int x,int y){//(x,y)的四元組的最大值
	return max(calc_unit(x,y,0),calc_unit(x,y,1));
}
int calc_left(int k,int d){//欽點第M行k列的值爲d
	setup[M][k]=d;
	setup[M][k+M]=d^setup[M][M];
	int ans=0;
	ans+=single_val(M,k)+single_val(M,k+M);
	for(int i=1;i<M;i++) ans+=calc_unit(i,k);
	return ans;
}
int calc_left(int k){//計算第M行k列的最大方案
	return max(calc_left(k,0),calc_left(k,1));
}
int calc_all(void){//第M列的1~M行已經決定了,求最大值
	for(int i=1;i<M;i++) setup[i+M][M]=setup[i][M]^setup[M][M];
	int ans=0;
	for(int i=1;i<=N;i++) ans+=single_val(i,M);
	for(int i=1;i<M;i++) ans+=calc_left(i);
	return ans;
}
void work(void){
	int ans=-INF;
	for(int s=0;s<(1<<M);s++){
		for(int i=1;i<=M;i++){
			setup[i][M]=((s>>(i-1))&1);
		}
		ans=max(ans,calc_all());
	}
	printf("%d\n",ans);
}
void read(void){
	scanf("%d",&N);
	M=(N+1)/2;
	for(int i=1;i<=N;i++){
		for(int j=1;j<=N;j++){
			scanf("%d",&A[i][j]);
		}
	}
}
int main(){
	//freopen("t1.in","r",stdin);
	read();
	work();
	return 0;
}


發佈了53 篇原創文章 · 獲贊 17 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章