 /**//*
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
搜索
最新评论

|
|