/**//* 4种模式,问选哪种模式,使得棋盘变为0而且开关个数最少
watashi博客说可以贪心,枚举第一行、列(与pattern有关!!!) 然后去判断是否无解、唯一解 他也提供了一种高斯消元做,然后再二进制枚举的方法
不确定自由变元个数是多少,他说: "根据另一种枚举加贪心的算法,枚举第一列只有2^15的情况,每次枚举,要么没解,要么只有一个解" 神奇~~
这里的高斯消元按照的方法: http://hi.baidu.com/czyuan_acm/blog/item/ebf41f8fdc0e1ee6f01f36e9.html “枚举k从0到equ – 1,当前处理的列为col(初始为0) ,每次找第k行以下(包括第k行),col列中元素绝对值 最大的列与第k行交换。 如果col列中的元素全为0,那么则处理col + 1列,k不变。”
我按照上面方法写,而且看了watashi的高斯消元,发现还可以列交换的,然后就方便回代了!!
注意为增广矩阵赋值时的小标范围控制,不要越界了!! */ #include<cstdio> #include<cstring> #include<algorithm> #include<vector>
using namespace std;
const int MAXN = 16*16; const int INF = 1000000000;
const char *light[] = { ".*.**..**.**", "***********.", ".*.**..**", };
bool T[16][16]; bool A[MAXN][MAXN] , x[MAXN];
#define getID(x,y) ((x)*m + (y)) #define get(x,y) ((x)&(1<<(y)))
int guass(bool A[][MAXN] , int n) { int row , col; for(row = 0 , col = 0 ; col < n ; col++)//make sure (< row , < col) = 0 { int id = row; for(;A[id][col] == false && id < n;id++);
if(id == n) continue; //all A[i][col] = 0
if(id != row) { for(int j = col ; j <=n ; j++) swap(A[row][j] , A[id][j]); }
if(col != row)//列交换 为了使得最后的矩阵前面的对角线都为1,方便回代 { //但这样会影响最后的解x[],因为被交换过了,但这种题不需要求出x[] for(int i = 0 ; i < n ; i++) swap(A[i][row] , A[i][col]); } for(int i = row + 1; i < n; i++) { if(A[i][row] == false)continue; for(int j = row; j <= n; j++) A[i][j] ^= A[row][j]; } row++; } return row; }
int cal(bool A[][MAXN] , bool x[] , int n , int num) { int all = 0; for(int i = 0 ; i < num ; i++) all += x[n-1-i]; for(int i = n-1-num ; i >= 0 ; i--) { int y = A[i][n]; for(int j = n-1; j > i ; j--) y ^= (A[i][j]&x[j]); x[i] = y; all += x[i]; } return all; }
int solve(bool A[][MAXN] , int n) { int row = guass(A,n);
for(int i = row ; i < n ; i++) { if(A[i][n])return INF;//no solution } int num = n - row; int limit = 1<<num , ans = INF; for(int i = 0 ; i < limit ; i++) { for(int j = 0 ; j < n ; j++) x[j] = false; for(int j = 0 ; j < num ; j++) { if(get(i,j)) { x[n-1-j] = 1; } } ans = min(ans , cal(A,x,n,num)); } return ans; }
int main() { #ifdef ONLINE_JUDGE #else freopen("in","r",stdin); //freopen("out","w",stdout); #endif
int n , m; while( scanf("%d%d",&n,&m) , n || m) { int nm = n*m;
char str[100];
for(int i = 0 ; i < n ; i++) { scanf("%s",str); for(int j = 0 ; j < m ;j++) { T[i][j] = (str[j] == '1') ; } } int ans = INF , pattern; for(int k = 0 ; k < 4; k++) { memset(A,0,sizeof(A)); //build A[][] for(int i = 0; i < n; i++) for(int j = 0 ; j < m ; j++) { for(int ii = 0; ii < 3; ii++) for(int jj = 0 ; jj < 3; jj++) { if(i-1+ii>=0 && j-1+jj>=0 && i-1+ii < n && j-1+jj < m)//注意范围控制!也得<n,m { A[getID(i-1+ii , j-1+jj)][getID(i,j)] = (light[ii][3*k+jj] == '*'); } } A[getID(i,j)][nm] = T[i][j]; }
/**//* puts("A::::"); for(int ii = 0 ; ii < nm ;ii++) { for(int jj = 0 ;jj<= nm;jj++) printf("%d ",A[ii][jj]); puts(""); }*/
int _ans = solve(A,nm);
//printf("%d %d\n",k+1,_ans);
if(_ans < ans) { ans = _ans; pattern = k+1; } }
if(ans >= INF)puts("Impossible"); else printf("%d %d\n",pattern,ans); } return 0; }
|
|
常用链接
随笔分类
Links
搜索
最新评论
|
|