Flip Game
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 37427   Accepted: 16288

Description

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: 
  1. Choose any one of the 16 pieces.
  2. 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).

Consider the following position as an example:

bwbw 
wwww 
bbwb 
bwwb 
Here "b" denotes pieces lying their black side up and "w" denotes pieces lying their white side up. If we choose to flip the 1st piece from the 3rd row (this choice is shown at the picture), then the field will become:

bwbw 
bwww 
wwwb 
wwwb 
The goal of the game is to flip either all pieces white side up or all pieces black side up. You are to write a program that will search for the minimum number of rounds needed to achieve this goal. 

Input

The input consists of 4 lines with 4 characters "w" or "b" each that denote game field position.

Output

Write to the output file a single integer number - the minimum number of rounds needed to achieve the goal of the game from the given position. If the goal is initially achieved, then write 0. If it's impossible to achieve the goal, then write the word "Impossible" (without quotes).

Sample Input

bwwb
bbwb
bwwb
bwww

Sample Output

4

Source

题目大意

  有$4\times 4$的棋盘,上面的棋子一面是黑的,一面是白的。规定翻转一个棋子的同时也要翻转它的上、下、左、右的棋子,问给定一个棋盘的棋子状态,至少需要翻转多少个棋子,能使得所有棋子都是白的或黑的(面在上)。

基本思路

一、暴力搜索出奇迹:

  1、首先要明确一点:这个翻棋子就像按位异或一样,如果一列数里面有两个数是相等的,那把它们全都异或起来,根据结合率就相当于在这过程中有一个数与自身异或,结果为0。把这两个相等的数去掉对最终异或结果是没有影响的。同样看这个翻棋子,它只有两个面,翻一次由黑面在上转为白面在上,或者白转黑,但翻两次就恢复原来的样子了,翻没翻是一样的,除非再翻多一次,甚至是奇数次,但这没有必要也没有意义。

  2、数据规模小,考虑我们最少不翻任何棋子(初始状态就是全白或全黑),最多只翻16个棋子(再翻就有棋子被翻两次了),因此总的状态数为$C^{0}_{16}+C^{1}_{16}+\cdots+C^{16}_{16}=2^{16}$,直接枚举或搜索即可。

  3、考虑要求的是最小值,因此从翻$0$个开始,搜索翻$i$个,直到翻$16$个,中间搜索到即为最小值,翻16个都不满足即输出$Impossible$。

  4、由于棋子都只有黑、白两面,可以用0、1表示,因此可以位压缩成一个数字来进行判断,翻棋子的操作可使用位运算,有两种方法:

    方法一:每一行压缩一个数字,对第$i$行第$j$列棋子进行翻转,比如$j=2$,则$i-1$、$i+1$行的棋子应该和4(0100)相异或(与1异或切换状态,与0异或不改变),而第$i$行棋子应与14(1110)相异或。

    方法二:只有16个棋子,一个int型变量就能存下这16个0/1了,所以可以直接压缩成一个数字。如$i=2, j=2$,则与20032(0100 1110 0100 0000)相异或,不过手算16位的状态是比手算4位烦一点点。

  5、搜索过程中要注意搜过的位置不需要再搜了,所以在函数里控制一下$i$、$j$,当然实现并不唯一。还要注意如果没搜成功,把棋子再翻(flip)一遍,这样就能恢复原样了。不需要memcpy,那是很蠢的做法。

方法一代码:

 #include <stdio.h>

 int field[]={};
int state[][]={{,,,},{,,,}}; void read() {
for(int i=; i<=; i++) {
for(int j=; j<=; j++) {
field[i]<<=;
if(getchar()=='b')
field[i]|=;
}
getchar();
}
} void flip(int i, int j) {--j;
field[i-]^=state[][j];
field[i] ^=state[][j];
field[i+]^=state[][j];
} bool check() {
return (field[]==||field[]==)
&& field[]==field[]
&& field[]==field[]
&& field[]==field[];
} bool find(int n, int i, int j) {
if(n==) return check();
j+=; if(j>) i+=, j=;
if(i>) return false;
for(; i<=; i++) {
for(; j<=; j++) {
flip(i, j);
if(find(n-,i,j))
return true;
flip(i, j);
}
j=;
}
return false;
} void work() {
for(int i=; i<=; i++)
if(find(i,,)) {
printf("%d\n", i);
return;
}
puts("Impossible");
} int main() {
read();
work();
return ;
}

POJ 1753 方法一

方法二代码(首先打个表):

 void init() {
for(int i=; i<=; i++) {
int v=, k=<<(i-); v|=k;
if((i+)%!=) v|=k<<;
if((i-)%!=) v|=k>>;
if(i>) v|=k>>;
if(i<) v|=k<<;
printf("%d,",v);
}
}

很丑的打表

然后就可以拿表去水了,当然直接判断也是可以的,丑。

 #include <stdio.h>

 int field;
int state[]={,,,,,,,,,,,,,,,}; void read() {
for(int i=; i<; i++) {
for(int j=; j<; j++) {
field<<=;
if(getchar()=='b')
field|=;
}
getchar();
}
} void flip(int i) {
field^=state[i];
} bool check() {
return field==0x0000||field==0xFFFF;
} bool find(int n, int i) {
if(n==) return check();
//if(i>=16) return false;
for(; i<; i++) {
flip(i);
if(find(n-,i+))
return true;
flip(i);
}
return false;
} void work() {
for(int c=; c<=; c++)
if(find(c,)) {
printf("%d\n", c);
return;
}
puts("Impossible");
} int main() {
read();
work();
return ;
}

POJ 1753 方法二

二、枚举

  1、这题应该容易想搜索,当然枚举也是比较简单能想到的。我们还是像前面方法二那样位压缩成一个数,如果不能压成一个int的话这题当然也用不了枚举。需要考虑的是如何实现$C^i_{16}$,也就是$16$个选$i$个$(i\in [0, 16])$,考虑我选哪几个棋子也表示成0/1,选择翻转的棋子我用1表示,比如要选择第1个、第3个、第5个和第6个,那就是11 0101的状态。这样枚举就很方便了,枚举值范围0x0000~0xFFFF。

  2、同样像上面方法二那样打个表,对于每个枚举的状态,用位与运算求出哪个位是1(哪个棋子要翻转),然后根据打表的数据对输入的棋盘进行异或运算。过程中对翻转后棋盘全黑或全白的情况求最少翻转数。

  3、可以顺手再打 1<<0 ~ 1<<15 的表。

 #include <stdio.h>

 int field;
int state[]={,,,,,,,,,,,,,,,};
int bit[]={,,,,,,,,,,,,,,,}; void read() {
for(int i=; i<; i++) {
for(int j=; j<; j++) {
field<<=;
if(getchar()=='b')
field|=;
}
getchar();
}
} bool check() {
return field==0x0000||field==0xFFFF;
} int minn=0xFF;
void work() {
for(int flip=; flip<=0xFFFF; flip++) {
int temp=field, cnt=;
for(int i=; i<; i++)
if(flip&bit[i]) {// flip&(1<<i)
field^=state[i];
++cnt;
}
if(check()&&minn>cnt) minn=cnt;
field=temp;
}
} void print() {
if(minn==0xFF) puts("Impossible");
else printf("%d\n", minn);
} int main() {
read();
work();
print();
return ;
}

POJ 1753 枚举

三、高斯消元法

  1、基本想法是,令a=棋盘状态矩阵,b=最终各棋子的状态,ax=b解出x=要翻转的棋子,数一下x里面1的数量就是翻转的棋子数了。因为最终状态可以是全黑或全白,因此需要对b取两次值,做两次消元。

  2、但是你会发现,这题会经常出现无穷多解的情况,也就是存在自由变元。因此需要枚举or搜索这些自由变元的值。

  (代码目前没交,待更新)

——原创by BlackStorm,转载请注明出处。

本文地址:http://www.cnblogs.com/BlackStorm/p/5231470.html

POJ 1753. Flip Game 枚举or爆搜+位压缩,或者高斯消元法的更多相关文章

  1. poj 1753 Flip Game 枚举(bfs+状态压缩)

    题目:http://poj.org/problem?id=1753 因为粗心错了好多次……,尤其是把1<<15当成了65535: 参考博客:http://www.cnblogs.com/k ...

  2. 枚举 POJ 1753 Flip Game

    题目地址:http://poj.org/problem?id=1753 /* 这题几乎和POJ 2965一样,DFS函数都不用修改 只要修改一下change规则... 注意:是否初始已经ok了要先判断 ...

  3. POJ 2965. The Pilots Brothers' refrigerator 枚举or爆搜or分治

    The Pilots Brothers' refrigerator Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22286 ...

  4. POJ 1753 Flip Game(高斯消元+状压枚举)

    Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 45691   Accepted: 19590 Descr ...

  5. POJ 1753 Flip Game DFS枚举

    看题传送门:http://poj.org/problem?id=1753 DFS枚举的应用. 基本上是参考大神的.... 学习学习.. #include<cstdio> #include& ...

  6. POJ 1753 Flip Game (DFS + 枚举)

    题目:http://poj.org/problem?id=1753 这个题在開始接触的训练计划的时候做过,当时用的是DFS遍历,其机制就是把每一个棋子翻一遍.然后顺利的过了.所以也就没有深究. 省赛前 ...

  7. POJ 1753 Flip Game(二进制枚举)

    题目地址链接:http://poj.org/problem?id=1753 题目大意: 有4*4的正方形,每个格子要么是黑色,要么是白色,当把一个格子的颜色改变(黑->白或者白->黑)时, ...

  8. POJ - 1753 Flip Game(状压枚举)

    https://vjudge.net/problem/POJ-1753 题意 4*4的棋盘,翻转其中的一个棋子,会带动邻接的棋子一起动.现要求把所有棋子都翻成同一种颜色,问最少需要几步. 分析 同一个 ...

  9. POJ 1753 Flip Game【枚举】

    题目链接: http://poj.org/problem?id=1753 题意: 由白块黑块组成的4*4方格,每次换一个块的颜色,其上下左右的块也会被换成相反的颜色.问最少换多少块,使得最终方格变为全 ...

随机推荐

  1. Asp.net中存储过程拖拽至dbml文件中,提示无法获得返回值

    Asp.net中存储过程拖拽至dbml文件中,提示无法获得返回值,去属性表中设置这时候会提示你去属性表中更改返回类型. 其实存储过程返回的也是一张表,只不过有时候存储过程有点复杂或者写法不规范的话不能 ...

  2. JSON.parse 与 eval() 对于解析json的问题

    1.eval()与JSOn.parse的不同 eval() var c = 1; //全局变量 var jsonstr1 = '{"name":"a",&quo ...

  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控

    系列目录 先补充一个平面化登陆页面代码,自己更换喜欢的颜色背景 @using Apps.Common; @{ Layout = null; } <!DOCTYPE html> <ht ...

  4. .NET Core采用的全新配置系统[6]: 深入了解三种针对文件(JSON、XML与INI)的配置源

    物理文件是我们最常用到的原始配置的载体,最佳的配置文件格式主要由三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

  5. zeptojs-跑马灯效果

    去年的时候了解学习了一下zeptojs,因为是移动js框架嘛还是必须要学习下的,里面封装的方法和jquery很类似,但是底层原理当然是不同的,这里展示的就是一个跑马灯效果,直接上代码和效果图: 样式不 ...

  6. 使用ETag进行session的降级

    回顾 在web后台开发中我们经常需要存储一些变量到session中进行暂存,最为特殊的就是"购物车",由于http的无状态特性,因此我们需要在客户端打上一个标记,唯一的标示客户端并 ...

  7. 实验:Oracle直接拷贝物理存储文件迁移

    实验目的:Oracle直接拷贝物理文件迁移,生产库有类似施工需求,故在实验环境简单验证一下. 实验环境: A主机:192.168.1.200 Solaris10 + Oracle 11.2.0.1 B ...

  8. 【无私分享:ASP.NET CORE 项目实战】目录索引

    简介 首先,我们的  [无私分享:从入门到精通ASP.NET MVC]   系列已经接近尾声,希望大家在这个过程中学到了一些思路和方法,而不仅仅是源码. 因为是第一次写博客,我感觉还是比较混乱的,其中 ...

  9. bodyparser

    今天在用bodyparser时,遇到了如下问题: 首先科普下bodyparser的作用:它用于解析客户端请求的body中的内容,内部使用JSON编码处理,url编码处理以及对于文件的上传处理. 现在继 ...

  10. MySql 修改列的注释信息的方法

    1. 问题     已经有很多数据的按照业务逻辑分表的一系列表修改一个字段(类型,如-1:默认值,1:表示'人员id',2:表示'公司id')的注释2. 解决方法     1> 使用alter ...