题目描述:
翻转0位置显然是翻转0 1 4这样 需要与 0x13异或 不推荐有些人的加法,明显按位异或才是对的,加法还要去模不太建议
l为list对象输入my([0,1,4]) ....一直到my([11,14,15])就得到了一个数组,翻转哪一位的时候异或就好了,下面是全部的代码:
思想很简单,就是遍历所有的状态空间0-65535每次都是从16个点中发散出去,有些人的代码很有意思的说,这个queue长度有限制的,所以自己数组操作,其实完全没有必要,STL的queue一样用的很好!每一个当前状态进入队列,然后对队列的首元素进行判断,如果首元素就是终止状态自然返回true,如果队列最后遍历完了,也没有发现当然返回false,如果某一部找到了出口,那这一步自然就是最短的步长,因为这是BFS,如果不是最短,那采用这种算法的人的思路就应该理一理了。
Flip game is played on a rectangular 4x4 field with two-sided pieces placed on each of its 16 squares.
One side of each piece is white and the other one is black and each piece is lying either it's black or white side up.
Each round you flip 3 to 5 pieces, thus changing the color of their upper side from black to white and vice versa.
The pieces to be flipped are chosen every round according to the following rules:
Choose any one of the 16 pieces.
Flip the chosen piece and also all adjacent pieces to the left, to the right, to the top, and to the bottom of the chosen piece (if there are any).
其实就是翻转每个位置,有些文章里面说到,每个位置只需要至多被翻转一次,这个是理论依据。毕竟一个位置变偶数次那么这个位置就复原了。
基本的思想应该是遍历2^16个状态,用int显然太浪费空间了,所以考虑位压缩。遍历其实有两种方式一种是BFS这是最基本的,还有简洁一点的就是DFS,都是遍历,BFS找到了最短路劲就退出,但是DFS不会,必须遍历完,但是只要保存下来最短的一次就好了,但是代码很简洁。
位翻转的思想是16位排成[15 14 13...3 2 1 0]这样的15位。
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
翻转0位置显然是翻转0 1 4这样 需要与 0x13异或 不推荐有些人的加法,明显按位异或才是对的,加法还要去模不太建议
对于每个位翻转应该异或的数,我用Python脚本表示出来了
def my(l):
ans = 0
for i in l:
ans += ( 2 ** i)
return ans
l为list对象输入my([0,1,4]) ....一直到my([11,14,15])就得到了一个数组,翻转哪一位的时候异或就好了,下面是全部的代码:
#include <iostream>
#include<fstream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAX = 16;
unsigned int current_state = 0;
int final_step = MAX;
const unsigned int flips[] = {19,39,78,140,305,626,1252,2248,
4880,10016,20032,35968,12544,29184,58368,51200};
void init(char str[]){
final_step = MAX;
current_state = 0;
for(int i=0;i<MAX;i++){
if(str[i] == 'b'){
current_state += (1 << i);
}
}
}
int flip(unsigned int state,int index){
return (state ^ flips[index]);
}
void mysearch(unsigned int state,int current_index,int step)
{
//if current_index > 15 应该置于后面
if(state == 0 || state == 65535){
if(step < final_step)
final_step = step;
return;
}
if(current_index > 15) return;
mysearch(state,current_index + 1,step);
state = flip(state,current_index);
mysearch(state,current_index + 1,step + 1);
return;
}
int main(int argc, char** argv) {
char str[MAX];
int number = 0;
char c;
while(cin >> c)
{
if(c==' ' || c == '\n')
continue;
str[number++] = c;
if(number == 16) break;
}
init(str);
mysearch(current_state,0,0);
if(final_step == MAX)
cout << "Impossible";
else
cout << final_step;
cout << endl;
return 0;
}
if current_index > 15 return;
如果这条语句放在myresearch的第一行,会带来问题,第15次翻转会直接return而不会判断这次翻转之后的状态。应该是先判断状态,然后在索引超过15之后再返回。
代码非常简洁。建议可以即用BFS又用DFS,毕竟可以更熟悉两种之间的差异,和某些特定场合通用情况!
下面我将列出我的BFS算法,就是广度优先的,首先要强调一点,有些代码在当访问某个被访问过的状态的时候,居然去修改改状态的步长,那一步是永远不会被执行的,因为广度优先,后面如果访问到了前面访问过的状态,无论如何后面的步长也会比前面的长,所以看着这些代码,觉得应该思考什么是BFS,不要写出外行的代码来。
先贴代码:
#include <iostream>
#include<fstream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int MAX = 16;
int final_step = MAX;
const int MAX_STATE = 65536;
int states[MAX_STATE];
int steps[MAX_STATE];
const unsigned int flips[] = {19,39,78,140,305,626,1252,2248,
4880,10016,20032,35968,12544,29184,58368,51200};
unsigned int init(char str[]){
final_step = MAX;
memset(states,0,sizeof(states));
memset(steps,0,sizeof(steps));
unsigned int current_state = 0;
for(int i=0;i<MAX;i++){
if(str[i] == 'b'){
current_state += (1 << i);
}
}
return current_state;
}
int flip(unsigned int state,int index){
return (state ^ flips[index]);
}
bool mysearch(unsigned int state)
{
int step = 0;
queue<unsigned int> qu;
qu.push(state);
states[state] = 1;
steps[state] = 0;
unsigned int front = 0;
while(!qu.empty())
{
front = qu.front();
qu.pop();
if(front == 0 || front == 65535)
{
final_step = steps[front];
return true;
}
unsigned int next = 0;
for(int i=0;i<MAX;i++)
{
next = flip(front,i);
if(!states[next])
{
states[next] = 1;
steps[next] = steps[front] + 1;
qu.push(next);
}
}
}
return false;
}
int main(int argc, char** argv) {
char str[MAX];
int number = 0;
char c;
while(cin >> c)
{
if(c==' ' || c == '\n')
continue;
str[number++] = c;
if(number == 16) break;
}
unsigned int current_state = init(str);
bool ans = mysearch(current_state);
if(!ans)
cout << "Impossible";
else
cout << final_step;
cout << endl;
return 0;
}
思想很简单,就是遍历所有的状态空间0-65535每次都是从16个点中发散出去,有些人的代码很有意思的说,这个queue长度有限制的,所以自己数组操作,其实完全没有必要,STL的queue一样用的很好!每一个当前状态进入队列,然后对队列的首元素进行判断,如果首元素就是终止状态自然返回true,如果队列最后遍历完了,也没有发现当然返回false,如果某一部找到了出口,那这一步自然就是最短的步长,因为这是BFS,如果不是最短,那采用这种算法的人的思路就应该理一理了。