[Usaco2007 Open]Fliptile 翻格子游戏题解

  

问题 B: [Usaco2007 Open]Fliptile 翻格子游戏

时间限制: 5 Sec  内存限制: 128 MB

题目描述

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M x N grid (1 <= M <= 15; 1 <= N <= 15) of square tiles, each of which is colored black on one side and white on the other side. As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make. Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".

 约翰知道,那些高智力又快乐的奶牛产奶量特别高.所以他做了一个翻瓦片的益智游戏来娱乐奶牛.在一个M×N(1≤M,N≤15)的骨架上,每一个格子里都有一个可以翻转的瓦片.瓦片的一面是黑色的,而另一面是白色的.对一个瓦片进行翻转,可以使黑变白,也可以使白变黑.然而,奶牛们的蹄子是如此的巨大而且笨拙,所以她们翻转一个瓦片的时候,与之有公共边的相邻瓦片也都被翻转了.那么,这些奶牛们最少需要多少次翻转,使所有的瓦片都变成白面向上呢?如杲可以做到,输出字典序最小的结果(将结果当成字符串处理).如果不能做到,输出“IMPOSSIBLE”.

输入

* Line 1: Two space-separated integers: M and N

* Lines 2..M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white

    第1行输入M和N,之后M行N列,输入游戏开始时的瓦片状态.0表示白面向上,1表示黑面向上.

输出

* Lines 1..M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.

    输出M行,每行N个用空格隔开的整数,表示对应的格子进行了多少次翻转.

样例输入

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

样例输出

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0 OUTPUT DETAILS: After flipping at row 2 column 1, the board will look like:
0 0 0 1
1 0 1 0
1 1 1 0
1 0 0 1 After flipping at row 2 column 4, the board will look like:
0 0 0 0
1 0 0 1
1 1 1 1
1 0 0 1 After flipping at row 3 column 1, the board will look like:
0 0 0 0
0 0 0 1
0 0 1 1
0 0 0 1 After flipping at row 3 column 4, the board will look like:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0 Another solution might be:
0 1 1 0
0 0 0 0
0 0 0 0
0 1 1 0
but this solution is lexicographically higher than the solution above.

  考试第二题,由于有IMPOSSIBLE保底分,果断先做第三题,结果打完发现连IMPOSSIBLE都没时间打了,

/(ㄒoㄒ)/~~,借祥子的一句话,我招谁惹谁了!

  这道题先膜一下QTY_大佬,给我讲明白了,首先这道题一定是搜索,应该不用解释吧,那么这道题最棘手的是我把它翻过来它又会把相邻的几个翻过去,这就会是一个不断绕的过程了,莫名想到了网络流“王者之剑”,于是每个人都会有一个愿望,他如果每次只翻动一个格子就好了,于是乎我们可以注意到我每翻一个格子它的上下层只改变一个,那我们能否利用这个特性去搞他呢,答案是肯定的。

  由于是搜索题,我们不必去管每一次搜索是否一定对,而是去试暴力,那我们便可去枚举第一行我怎么翻,因为第一行翻法确定之后就可以采取近似贪心的策略,挨行去翻,只要上一行同一列的位置为1,我就翻它所对应的本行的位置知道最后一行,只要在最后加一个判断,去判断它最后一行是否都为0,是的话就是可行解,是的,可行解,因为题目要求字典序最小,这个我们待会再说,那我们怎么枚举第一行的翻法呢,感谢QTY_,状压就可以了,时间复杂度也是很好算的,状压是2^15,枚举就是枚举每个格子,最大才225,没毛病。

  那么我们再来解释下字典序这个烦人的东西,说实在的,题目给的字典序到底是怎么来的只能自己推,由样例和注释可知,即使1的个数一样也是有区别的,那么观察可发现样例输出的第一个1位置比注释中的解靠前,因此可以大胆的猜测可以以进制的思想去搞它。

  但这道题还没完,细心的人可能会发现本题中并未保证每个格子翻动次数只为1或0,也就是说每个格子在解中可能翻动了好几次,而按照上面的说法貌似只有0和1,是不是欺负数据水呢?当然不是假设有一种解为:

              0 1

              2 0

那么它完全等价为

              0 1

              0 0

以此类推,大于1只是可行解,不是最优解。

  

 #include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,b[][];
int a[][],an[][];
int mn=0x7fffffff,mi,ans[][];
void dfs(int x){
memset(an,,sizeof(an));
memcpy(b,a,sizeof(a));
for(int i=;i<=n;i++)
{
if((<<(i-))&x)
{
an[][i]=;
b[][i]^=;
b[][i-]^=;
b[][i+]^=;
b[][i]^=;
}
}
for(int i=;i<=m;i++)
{
for(int j=;j<=n;j++)
{
if(b[i-][j])
{
an[i][j]=;
b[i-][j]^=;
b[i][j]^=;
b[i][j-]^=;
b[i][j+]^=;
b[i+][j]^=;
}
}
}
bool yx=;
for(int i=;i<=n;i++)
{
if(b[m][i])
{
yx=;
break;
}
} if(yx)
{ int js=,be=;
for(int i=;i<=m;i++)
{
for(int j=;j<=n;j++)
{
if(an[i][j])
{
js++;
if(!be)
be=(i-)*+j;
}
}
}
if(js<mn)
{
mn=js;
mi=be;
memcpy(ans,an,sizeof(an));
}
else if(js==mn&&be<mi)
{
mi=be;
memcpy(ans,an,sizeof(an));
}
}
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=m;i++)
{
for(int j=;j<=n;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=;i<(<<n);i++)
{
dfs(i);
}
if(mn==0x7fffffff)
{
printf("IMPOSSIBLE\n");
}
else
{
for(int i=;i<=m;i++)
{
for(int j=;j<=n;j++)
printf("%d ",ans[i][j]);
printf("\n");
}
}
return ;
}
上一篇:java枚举类型的实现原理


下一篇:如何使用XMPP Java向用户发送聊天邀请?