51nod1316 回文矩阵 并查集+枚举

1316 回文矩阵
题目来源: TopCoder
基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题
收藏
关注
 一个N*M的矩阵A完全由0与1两个数字组成(0 (1)将矩阵中的任一项A[i][j]改为数字1;
(2)将矩阵中的任一项A[i][j]改为数字0;
现在给出初始的矩阵A,要求经过最少次操作,使矩阵A中至少有RowCount行是回文的,同时存在至少ColumCount列是回文的。输出这个最少操作的次数。
矩阵中第r行是回文的,指序列{A[r][0],A[r][1],...A[r][M-1]}回文,即所有的 i 有A[r][i]=A[r][M-1-i];
矩阵中第c列是回文的,指序列{A[0][c],A[1][c],...A[N-1][c]}回文,即所有的 i 有A[i][c]=A[N-1-i][c].

例如4x4的矩阵如下:
0000
1000
1100
1110
要求RowCount = 2,且ColumCount = 2.
可以将A[3][0]改为0,使第0行与第3行回文,同时第0列与第3列回文。变化后如下:
0000
1000
1100
0110
Input
第一行两个正整数,表示RowCount,ColumCount,且0<=RowCount<=N,0<=ColumCount<=M.
第二行一个整数N,即矩阵的行数,1<=N<=8.
之后有N行,每行一个由M个‘0’、‘1’字符构成的字符串,表示N*M矩阵的信息。
Output
一个整数,表示最少操作的次数。
Input示例
2 2
4
0000
1000
1100
1110
Output示例
1
思路:数据范围8以下的,所以可以枚举所有可能为回文的行排列和列排列,在每一种情况下找到满足这种情况的一定要相等的数字,加入集合,最后统计每个集合需要操作的数量,相加,对所有情况取最小。
For example: 
就按照样例输入: 
2 2
4
0000
1000
1100
1110
假设我们枚举到了行回文是第1和第4行,列回文是第1和第4列(所有情况中的其中一个)。那么我上面相同颜色标记的数字一定要相等。所以我就用了并查集,将应该相等的数字放到一个集合,最后看看这种情况下每个集合中0少还是1少(0为0或1为0说明这个集合不需要操作,满足回文性质),取0和1数量小的相加就是这种情况下的最小操作次数。
Code:

#include 
#include 
#include 
using namespace std;
const int AX = 100;
char s[AX][AX];
int n , m;
int visR[AX];
int visC[AX];
int vis[AX];
int pre[AX];
int Rc , Cc;
int res ;
int find( int x ){
	return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void mix( int x , int y ){
	int xx = find(x);
	int yy = find(y);
	if( xx != yy ){
		pre[xx] = yy;
		return ;
	}
}

void solve(){
	for( int i = 0 ; i <= n * m ; i++ ) pre[i] = i;
		for( int i = 0 ; i < n ; i ++ ){
			if( visR[i] ){
				for( int j = 0 ; j < m ; j ++ ){
					int x = i * m + j;
					int y = i * m + m - j - 1;
					if( find(x) != find(y) ){
						mix( x , y );
					}
				}
			}	
		}
		for( int j = 0 ; j < m ; j++ ){
			if( visC[j] ){
				for( int i = 0 ; i < n ; i++ ){
					int x = i * m + j;
					int y = ( n - 1 - i ) * m + j;
					if( find(x) != find( y ) ){
						mix( x , y );
					}
				} 
			}
		}
		int tmp = 0 ;
		for( int k = 0 ; k < n * m ; k ++ ){
			int t = 1;
			int one = 0 , zero = 0;
			for( int i = 0 ; i < n ; i++ ){
				for( int j = 0 ; j < m ; j ++ ){
					int kk = i * m + j ;
					if( find(kk) == k ){
						t++;
						s[i][j] == '1' ? (one++) : (zero++);
					}
				}
			}
			if( t > 1 ){
				tmp += min(zero,one);
			}
		}
		res = min( res , tmp );
	}

	void createC( int x , int tot ){  
		if( x >= m ) return;
		if( tot > Cc ) return;
		if( tot + m - 1 - x < Cc ) return; 
		visC[x] = 1;  
		if( tot == Cc ){
			solve();
			return;  
		}  
		for( int i = x + 1 ;  i < m ; i++ ){  
			if( !visC[i] ){  
				createC( i , tot + 1 );  
				visC[i] = 0;  
			}  
		}  
	}  

	void createR( int x , int tot ){  
		if( x >= n ) return ;
		if( tot > Rc ) return;
		if( tot + n - 1 - x < Rc  ) return;
		visR[x] = 1;  
		if( tot == Rc ){
			for( int j = 0 ; j < m ; j++ ){
				memset( visC , 0 ,sizeof(visC));
				createC( j , 1 ); 
			}  
			return;  
		}  
		for( int i = x + 1 ;  i < n ; i++ ){  
			if( !visR[i] ){  
				createR( i , tot + 1 );  
				visR[i] = 0;  
			}  
		}  
	}  

	int main(){
		scanf("%d%d",&Rc,&Cc);
		scanf("%d",&n);
		res = 100000;
		for( int i = 0 ; i < n ; i++ ){
			scanf("%s",s[i]);
		}
		if( !Rc && !Cc ){
			cout << 0 << endl;
			return 0;
		}
		m = strlen(s[0]);
		for( int i = 0 ; i < n ; i++ ){
			memset( visR , 0 ,sizeof(visR) );
			createR( i , 1 );
		}
		cout << res << endl;
		return 0 ;
	}


你可能感兴趣的:(全排列,并查集)