POJ 1753 Flip Game 解题报告

这一题,要用位运算,我用的是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][N] = (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][N])	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]][N];
			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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值