zoukankan      html  css  js  c++  java
  • [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
    ",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;
    }
    


  • 相关阅读:
    iOS故事板下使用代码跳转页面
    眼见不一定为实
    UITableView
    iOS 备忘录
    统计代码行数
    iOS - 图片合成
    UI 素材
    xcode中xib使用小技巧
    svn不能提交静态库解决方案
    在webSocket中获取shiro报错
  • 原文地址:https://www.cnblogs.com/wmdcstdio/p/7554239.html
Copyright © 2011-2022 走看看