您的位置:首页 > 其它

POJ 1753 Flip Game

2014-04-16 17:32 295 查看
这一题,要用位运算,我用的是BFS,方法也很显示,不断从最开始的状态开始扩展,直到找到全白或全黑的情况再退出,visited数组用来判断是不是已经出现过,防止重复的循环。

BFS+位运算

#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
int matrix;
struct Piece
{
int m;
int c;
};
int wei[16] = {0xC800,0xE400,0x7200,0x3100,0x8C80,0x4E40,0x2720,0x1310,0x08C8,0x04E4,0x0272,0x0131,0x008C,0x004E,0x0027,0x0013};
int main(){
int i,j;
bool visited[65536];
matrix=0;
char temp[5];
queue<Piece> qqq;
for(i=0;i<4;i++){
scanf("%s",temp);
for(j=0;j<4;j++){
if(temp[j]=='b')
matrix|=(1<<(i*4+j));
}
}
memset(visited,false,sizeof(visited));
int ccc=-1;
if(matrix==0 || matrix==0Xffff){
printf("0\n");
return 0;
}
Piece start;
start.m=matrix;
start.c=0;
qqq.push(start);
visited[matrix]=1;
while(!qqq.empty()){
Piece p=qqq.front();
if(p.m==0 || p.m==0Xffff){
ccc=p.c;
break;
}
qqq.pop();
p.c++;
for(i=0;i<16;i++){
Piece p2=p;
p2.m^=wei[i];
if(visited[p2.m]){
continue;
}
visited[p2.m]=true;
qqq.push(p2);
}
}
if(ccc!=-1){
printf("%d\n",ccc);
}
else
printf("Impossible\n");
return 0;
}


用DFS也可以解决这道题,代码引用别人的。

DFS+位运算

#include <iostream>
using namespace std;

int fanzhuan[16];
int mychess;
char chess[18];
//翻转第i个,利用了按位异或,与1异或相当于翻转,与0异或相当于不转
int turn[16] = {0xC800,0xE400,0x7200,0x3100,0x8C80,0x4E40,0x2720,0x1310,0x08C8,0x04E4,0x0272,0x0131,0x008C,0x004E,0x0027,0x0013};

//我是核心部分哦~
bool dfs(int now,int level,int last )//从last开始
{
if(now==level)//现在已经是第level次翻转了
{
if(mychess==0||mychess==0x00ffff)
return true;
else
return false;
}else{
//还剩16-last个可供翻转,但是我还想要翻转level-now个
if(16-last<level-now)
return false;
for(int i =last;i<16;i++)
{
if(fanzhuan[i])//已经翻转过了,没必要再翻转了
continue;

fanzhuan[i]=1;//翻转i
mychess^=turn[i];

if(dfs(now+1,level,i))//如果我把第i个翻转后,继续进行会成功,那么返回true。如果将来不成功,再把i转回来
return true;

fanzhuan[i]= 0;//既然不成功,转回来呗
mychess^=turn[i];
}
return false;
}
}

int main()
{
int i=0;
//输入字符
while(i<16)
{
cin>>chess+i;
i+=4;
}
//将字母转化为数字
mychess =0;
for(i=0;i<16;i++)
{
if(chess[i]=='b')//b->1
mychess |=(1<<(15-i));
}
if(mychess!=0&&mychess!=0xffff)
{
for(i=1;i<=16;i++)
{
//选取i个翻转
memset(fanzhuan,0,sizeof(fanzhuan));
if(dfs(0,i,0))
break;
}
if(i>16)
printf("Impossible\n");
else
printf("%d\n",i);
}else
{
printf("0\n");
}
cin>>i;
return 0;
}


效率最高的是高斯消元的方法,我暂时不会,努力学习中,代码也是别人的。

高斯消元

#include <iostream>
using namespace std;
#define SIZE 20
#define N 16
#define INF (1<<20)
bool a[SIZE][SIZE]={0},isfree[SIZE]={0},x[SIZE]={0};	//isfree[i]表示x[i]为自由元
int pivot[SIZE]={0},freex[SIZE]={0},r[SIZE]={0};
//pivot[i]是第i个主元所在的列,r[i]为其所在的行;freex[i]是第i个自由元所在的列
char board[SIZE][SIZE];

int Gauss(int s)
{
int i,j,k,index,col,p,f,cnt;
bool tmp;

for (i=0;i<N;i++)
for (j=0;j<N;j++)
a[i][j]=0;

for (i=0;i<4;i++)			//构造系数矩阵A
for (j=0;j<4;j++)
{
k = 4*i+j;
a[k][k]=1;
if (i>0)		a[k][k-4]=1;		//上
if (i<3)		a[k][k+4]=1;		//下
if (j>0)		a[k][k-1]=1;		//左
if (j<3)		a[k][k+1]=1;		//右
a[k]
= (board[i][j]=='b')^s;
}

for (p=f=k=col=0; k<N && col<N; k++,col++)		//消元
{
for (i=k; i<N && !a[i][col]; i++);
if ( (index=i)==N )	{k--;isfree[col]=true;freex[f++]=col;continue;}

isfree[col] = false;
pivot[p] = col;
r[p++] = k;

if (index!=k)
for (j=col;j<=N;j++)
{tmp=a[k][j];a[k][j]=a[index][j];a[index][j]=tmp;}

for (i=k+1;i<N;i++)
if (a[i][col])
for (j=col;j<=N;j++)
a[i][j] ^= a[k][j];
}

for (i=k;i<N;i++)			//判定无解
if (a[i]
)	return INF;

int tot= (1<<f);
int res = INF;
for (k=0;k<tot;k++)		//枚举自由变量的取值。无自由元时也可以统一进来
{
index = k;			//自由变量取遍k的每一个二进制位
cnt = 0;
for (j=0;j<f;j++)
{
x[freex[j]] = (index & 1);
if (x[freex[j]])		cnt++;
index>>=1;
}

for (i=p-1;i>=0;i--)		//回代求得每一个(共p个)主元的值
{
x[r[i]] = a[r[i]]
;
for (j=N-1; j>pivot[i]; j--)
if (a[r[i]][j])		x[r[i]] ^= x[j];
if (x[r[i]])	cnt++;

}
if (cnt<res)	res=cnt;
}

return res;
}

int main()
{
int i,j,tmp,ans=INF;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
cin >> board[i][j];

if ( (tmp=Gauss(0))<ans )		ans=tmp;
if ( (tmp=Gauss(1))<ans )		ans=tmp;
if (ans!=INF)
cout << ans << endl;
else
cout << "Impossible" << endl;

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: