zoukankan      html  css  js  c++  java
  • 「Solution」Luogu P1225 黑白棋游戏

    关于这道题目的解法,其他题解已经讲得非常清楚,另外,我在代码中也做了简单的注释,应该很容易就能理解题目的做法。

    作为一道经典的位运算运用题目,我主要是想详细讲讲在这题中,位运算是如何使用的。

    如果你没有接触过位运算,参考OI-Wiki

    用了位运算才知道这东西有多爽

    位运算的使用

    这道题目中,bfs肯定需要进行判重,那么位运算就有很大的优势,因为棋盘布局一共只有(2^{16})种可能,而棋盘中每一个点恰好对应0或1,于是容易想到将四排棋盘状态压成一行,然后将其当做二进制转为十进制,用0到65535这65536个数对应每种状态。

    以下以样例为例,初始棋盘如下:

    1111
    0000
    1110
    0010
    

    将其先压成一行:

    1111000011100010
    

    这就是棋盘的状态的二进制码,将其转为十进制,即61666。

    为了方便进行位运算,我们为棋盘进行如下编号:

      0  1  2  3
      4  5  6  7
      8  9 10 11
     12 13 14 15
    

    我们需要进行的操作其实就是两种:

    1. 获取编号为(i)棋子的颜色
    2. 交换编号为(i)和编号为(j)的棋子

    对于操作1,用位运算表示为:(n >> (15 - i)) & 1

    原理很简单,比如我们想获取样例中编号为3的棋子颜色,那么我们将3号棋子移动到第0位上,移动的位数即为15 - i位,所以就有了n >> (15 - i)

    1111(000011100010)
    

    接下来,将其和1进行与运算,其实就是获得了第0位的数码。

    1111 & 0001 = 1
    

    操作1就实现了,在程序中,15 - i被定义为了ni

    对于操作2,用位运算表示为:n + (1 << (15 - j)) - (1 << (15 - i))

    同样拿样例举例,我们想要将编号为1的棋子向下移动,即为将编号为1的棋子与编号为1 + 4 = 5的棋子互换。

    为方便进行位运算,我们在程序中约定,被移动棋子只能是黑棋,目标棋子只能是白棋。

    在二进制数当中,我们要做的就是将某一位上的1改成0,将某一位上的0改成1

    很容易得到以下算式:

    [(1111000011100010)_2 - (100000000000000)_2 + (10000000000)_2 = (1011010011100010)_2 ]

    转换成位运算就是n + (1 << (15 - j)) - (1 << (15 - i))

    这样,我们就能在程序中使用位运算进行棋盘上棋子的移动操作了。

    Tip

    本题不能使用getchar!

    本题不能使用getchar!

    本题不能使用getchar!

    RE记录

    AC CODE

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int n; // 当前棋盘 
    	int step; // 计步 
    	string way; // 记录操作 
    	node(int n, int step, string way): n(n), step(step), way(way) {}
    };
    int start, final, n, step, newn;
    char t;
    string way, newway;
    bool vis[65536];
    queue<node>q;
    int main(){
    	for(int i = 0; i < 4; i++)
    		for(int j = 0; j < 4; j++)
    			cin >> t, start = start * 2 + (int)(t-'0');
    	for(int i = 0; i < 4; i++){
    		for(int j = 0; j < 4; j++)
    			cin >> t, final = final * 2 + (int)(t-'0');
    		getchar();
    	}
    	q.push(node(start, 0, ""));
    	vis[start] = true;
    	while(!q.empty()){
    		n = q.front().n, step = q.front().step, way = q.front().way;
    		if(n == final){
    			cout << step;
    			for(int i = 0; i < (signed)way.size(); i++){
    				if(i % 4 == 0) cout << endl;
    				cout << way[i];
    			}
    			return 0;
    		}
    		step++;
    		q.pop();
    		for(int i = 0; i < 16; i++){
    			int ni = 15 - i;
    			if(((n >> ni) & 1) == 0) continue; // 被移动棋只选黑棋 
    			// 左移 
    			if(i % 4 != 0){ // 棋子不能位于棋盘第一列 
    				int j = ni + 1;
    				if(((n >> j) & 1) == 0){ // 目标棋应是白棋 
    					newn = n + (1 << j) - (1 << ni);
    					if(!vis[newn]){
    						vis[newn] = true;
    						newway = way + (char)(i/4+1+'0');
    						newway += (char)(i%4+1+'0');
    						newway += (char)((15-j)/4+1+'0');
    						newway += (char)((15-j)%4+1+'0');
    						q.push(node(newn, step, newway));
    					}
    				} 
    			}
    			// 右移 
    			if(i % 4 != 3){ // 棋子不能位于棋盘第四列
    				int j = ni - 1;
    				if(((n >> j) & 1) == 0){
    					newn = n + (1 << j) - (1 << ni);
    					if(!vis[newn]){
    						vis[newn] = true;
    						newway = way + (char)(i/4+1+'0');
    						newway += (char)(i%4+1+'0');
    						newway += (char)((15-j)/4+1+'0');
    						newway += (char)((15-j)%4+1+'0');
    						q.push(node(newn, step, newway));
    					}
    				} 
    			}
    			// 上移 
    			if(i >= 4){ // 棋子不能位于棋盘第一行 
    				int j = ni + 4;
    				if(((n >> j) & 1) == 0){
    					newn = n + (1 << j) - (1 << ni);
    					if(!vis[newn]){
    						vis[newn] = true;
    						newway = way + (char)(i/4+1+'0');
    						newway += (char)(i%4+1+'0');
    						newway += (char)((15-j)/4+1+'0');
    						newway += (char)((15-j)%4+1+'0');
    						q.push(node(newn, step, newway));
    					}
    				} 
    			}
    			// 下移
    			if(i <= 11){ // 棋子不能位于棋盘第四行 
    				int j = ni - 4;
    				if(((n >> j) & 1) == 0){
    					newn = n + (1 << j) - (1 << ni);
    					if(!vis[newn]){
    						vis[newn] = true;
    						newway = way + (char)(i/4+1+'0');
    						newway += (char)(i%4+1+'0');
    						newway += (char)((15-j)/4+1+'0');
    						newway += (char)((15-j)%4+1+'0');
    						q.push(node(newn, step, newway));
    					}
    				} 
    			} 
    		}
    	}
    }
    /*
    1111
    0000
    1110
    0010
    1010
    0101
    1010
    0101
    */
    

    AC记录

    • 用时:59ms
    • 空间:1.00MB
  • 相关阅读:
    ACM_求f(n)
    ACM_四数之和
    jQuery 3D旋转展示焦点图
    jQuery+CSS3动画相册特效
    jQuery/CSS3实现Android Dock效果
    html5/CSS3鼠标滑过图片特效插件
    CSS3定时提示动画特效
    日期选择插件
    HTML5/CSS3淡入淡出滑块焦点图
    HTML5/CSS3动画下拉菜单
  • 原文地址:https://www.cnblogs.com/liuxizai/p/13781404.html
Copyright © 2011-2022 走看看