zoukankan      html  css  js  c++  java
  • 【9801】黑白棋游戏

    Time Limit: 3 second
    Memory Limit: 2 MB

    【问题描述】

    黑白棋游戏的棋盘由4*4方格阵列构成。棋盘的每一方格中放有1枚棋子,共有8枚白棋子和8枚黑棋子。这16枚棋子的每一种放置方案都构成一个游戏状态。在棋盘上拥有1条公共边的2个方格称为相邻方格。一个方格最多可有4个相邻方格。在玩黑白棋游戏时,每一步可将任何2个相邻方格中棋子互换位置。对于给定的初始游戏状态和目标游戏状态,编程计算从初始游戏状态变化到目标游戏状态的最短着棋序列。

    【输入格式】

    共有8行。前四行是初始游戏状态,后四行是目标游戏状态。每行4个数分别表示该行放置的棋子颜色,“0”表示白棋,“1”表示黑棋。

    【输出格式】

    第一行是着棋步数n,接下来n行,每行4个数分别表示该步交换棋子的两个相邻方格的位置。例如abcd表示将棋盘上(a,b)处的棋子与(c,d)处的棋子换位。

    【输入样例】

        1111
        0000
        1110
        0010
        1010
        0101
        1010
        0101
        

    【输出样例】

        4
        1222
        1424
        3242
        4344
    

    【题解】

    这题的难点在状态的判重上。需要用到二进制来表示棋子的状态。

    首先,预处理出2^1->2^16;存在一个数组中。需要的时候直接访问即可。

    然后把4行的棋子状态转变为一行。

    如输入样例 起始状态为 1111000011100010;一共16个字符。

    注意一开始的时候在字符串前加个空格。这样这串字符就从字符串的下标1开始了。

    然后for (int i = 1;i <= 16;i++) sum +=two_n[i]*(s[i]-'0');

    这样就把“二进制转化成10进制了”但注意真正的二进制转10进制需要逆序 且是从2^0开始的。

    但我们只需要用二进制的思想存储状态就可以了。

    然后bo[sum] = true,标记这个状态已经出现过。

    然后进行广搜。

    在广搜的时候不要把这段“十进制”转化成字符串。而是直接在这个“十进制”上进行操作。

    我们在搜的时候只要考虑两个变换方向即向右和向下。因为向左和向右会重复。

    同时我们要判断两个棋子颜色是否不同,如果颜色相同则移动毫无意义。

    然后for(int i = 1;i <= 16;i++)

    { if ( i不是4的倍数)就可以向右移动

    x = i+4;

    如果x <=16就可以向下移动。其中x和i对应了需要移动的棋子 }

    然后是变换的原理

    比如

    11100000000

    我们要把第4个0变成1,只要加上2^4就可以了。

    我们要把第2个1变成0,只要减去2^2就可以了。

    具体的操作请看代码

    【代码】

    #include <cstdio>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    const int maxdl = 100000;
    
    struct ss
    {
    	int x1,y1,x2,y2;	
    };
    
    int two_n[17],f,t,team[maxdl],step[maxdl],pre[maxdl];
    string s1,s2;
    bool panchong[131079*2] = {0};
    ss d_step[maxdl];
    
    void input_data(string & s1)
    {
    	s1 =" ";
    	string ss;
    	for (int i = 1;i <= 4;i++)
    		{
    			getline(cin,ss);
    			s1+=ss;		
    		}		
    }
    
    void init() //先预处理出2的1次方到2的16次方 
    {
    	two_n[0] = 1;
    	for (int i = 1;i <= 16;i++)
    		two_n[i] = two_n[i-1]*2;
    	for (int i = 1;i <= 16;i++) //把“二进制转换成10进制 "
    		f += (s1[i]-'0')*two_n[i];
    	for (int i = 1;i <= 16;i++)
    		t += (s2[i]-'0')*two_n[i];	
    }
    
    void output_ans(int temp) //用于递归输出方案。 
    {
    	if (temp == 1) //如果前一个是头指针则结束。这是递归终点 
    		return;
    	output_ans(pre[temp]);
    	printf("%d%d%d%d
    ",d_step[temp].x1,d_step[temp].y1,d_step[temp].x2,d_step[temp].y2);
    }
    
    void get_ans()
    {
    	int head = 0,tail =1;
    	team[1] = f; //team数组用于存储以“十进制”存储的状态 
    	step[1] = 0;
    	if (f == t) //如果初始状态和末状态相同,则直接输出0 
    		{
    			printf("0");
    			return;
    		}
    	panchong[f] = true; //标记这个状态已经找过 
    	while (head != tail) 
    		{
    			head++;
    			int ff = team[head],ss = step[head]; //取出状态和步骤数 
    			bool bo[17] = {0}; //用于“把十进制换成二进制” 
    			for (int i = 1;i <= 16;i++)
    				if ( (ff & two_n[i]) > 0) //如果之前存过2^i次方,在这个伪10进制转换成2进制后
    				//倒数第i+1位确实会变成1.所以和2^i进行与操作,如果这个位置上有棋子,最后结果会是大于0的。
    				//比如有0011这样的情况。第3,4位为1,我们转换成的伪十进制是24;
    				//之后我们要把这个棋子信息存进bo数组中
    				//我们枚举i,i到了3,用2^3和24进行与运算
    				//2^3的二进制是1000
    				//24的二进制是11000 ,可以看到第4位都是1,所以我们判断i=3时,bo[i] = 1(true);
    				//再枚举到4时
    				//2^4的二进制是10000
    				//24的二进制是11000 ,可以看到第5位都是1,所以我们判断i=4时,bo[i] = 1(true);
    				//这种虽然和二进制转10进制的正确做法不同,但可以用这种“错误”的方法来存储状态
    				//并且在用位运算进行“压缩”和“解压” 
    				//还有变换的例子。
    				//还是上面的0011
    				//伪十进制为24;
    				//如果我们想把第3个1变成0就减去2^3.
    				//就变成了16;
    				//再用伪方法转换成二进制。就变成0001了。 
    					bo[i] = true;
    			for (int i = 1;i <= 16;i++)
    				{
    					int temp,x;
    					
    					x= i + 4; //优先往下转换 
    					if (x <= 16)
    						{
    							if (bo[i] == (1-bo[x])) //如果那个位置的棋子颜色和当前枚举到的棋子颜色不同 
    								{
    									temp = ff; //対temp进行操作,不影响原数字 
    									if (bo[x] == 0)	//用加减法分别把0变成1,1变成0(二进制上的1/0) 
    										temp += two_n[x];
    											else
    												temp-=two_n[x];
    									if (bo[i] == 0)
    										temp +=two_n[i];
    											else
    												temp-=two_n[i];
    									if (!panchong[temp]) //变换完之后进行判重 
    										{
    											panchong[temp] = true;
    											tail++;
    											if ((i % 4) != 0) //这里要把一维坐标转换成二维的,注意几个能被4整除的数的判断即可 
    												d_step[tail].x1 = (i/4) + 1;
    													else
    														d_step[tail].x1 = i/4;
    											d_step[tail].y1 = ((i-1) % 4) + 1;
    											if ((x % 4)!=0)
    												d_step[tail].x2 = (x/4) + 1;
    													else
    														d_step[tail].x2 = x / 4;
    											d_step[tail].y2 = ((x-1) % 4) + 1;											
    											team[tail] = temp; //记录这个状态 
    											step[tail] = ss+1;					
    											pre[tail] = head; //记录前一个状态是什么 (最后输出方案) 
    											if (temp == t)
    												{
    													printf("%d
    ",ss+1);
    													temp = tail;
    													output_ans(temp); //用递归输出答案 
    													return;
    												}
    										}												
    								}
    						}					
    					
    					if ( (i % 4) !=0) //如果不是最右边的数字 
    						{
    							x = i+1;
    							if (bo[x] == (1-bo[i]))	//如果颜色和当前的不同 
    								{
    									temp = ff;
    									if (bo[x] == 0)	 //在temp上进行操作 
    										temp += two_n[x];
    											else
    												temp-=two_n[x];
    									if (bo[i] == 0)
    										temp +=two_n[i];
    											else
    												temp-=two_n[i];
    									if (!panchong[temp]) //如果之前没有找到过这个状态 
    										{
    											panchong[temp] = true;//进行判重					
    											tail++; //把这个步骤加入到队列当中 
    											if ( (i%4) !=0) //把一维坐标转换成二维坐标 
    												d_step[tail].x1 = (i/4) + 1;
    													else
    														d_step[tail].x1 = i/4;
    											d_step[tail].y1 = ((i-1) % 4) + 1;
    											if ((x % 4) != 0)
    												d_step[tail].x2 = (x/4) + 1;
    													else
    														d_step[tail].x2 = x /4;
    											d_step[tail].y2 = ((x-1) % 4) + 1;
    											team[tail] = temp;
    											step[tail] = ss+1;	//步骤数+1 
    											pre[tail] = head;
    											if (temp == t) //如果找到了目标状态 则停止。 
    												{
    													printf("%d
    ",ss+1);
    													temp =tail;
    													output_ans(temp);
    													return;
    												}
    										}	
    								}
    						}
    				}
    		}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data(s1);
    	input_data(s2);
    	init();
    	get_ans();
    	return 0;	
    }


  • 相关阅读:
    Nginx 配置文件
    Nginx 目录结构
    Nginx 功能模块
    logrotate日志管理工具
    Nginx 编译安装
    CentOS7安装Tomcat
    springcloud如何实现服务的平滑发布
    springcloud zuul聚合微服务的swagger文档
    alibaba的springcloud孵化器项目
    springcloud灰度发布实现方案
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632365.html
Copyright © 2011-2022 走看看