Fliptile POJ3279 二进制压缩枚举 解题报告
2015-04-24 19:27
309 查看
D -
Fliptile
Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%I64d
& %I64u
SubmitStatusPracticePOJ
3279
Description
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 × 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".
Input
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
Output
Lines 1.. M: Each line contains
N space-separated integers, each specifying how many times to flip that particular location.
Sample Input
Sample Output
做这题之前,我们首先需要理解一个现象:除了最后一行,其他任何一行上的1都可以通过下一行的翻转转换成0。
也就是说,除了最后一行外,我们总是可以通过翻转,将前n-1行翻成全0。
只要按照这样的原则:对于某一个位置x,如果它的上一个是1,就翻转它,如果是0,就不翻转。
知道了这个,我们应该就能想到,第一行的翻法直接就决定了后面的所有的翻法,这就是我们解决这道题目的思路,即枚举第一行所有可能的翻法,这里,如果用循来枚举,未免太浪费时间了,所以,这里采取了一种二进制压缩的方法。
首先让我们来学习一下左移<<的概念,其实很好理解,就是将一个数转换为二进制,然后向左移动若干位,然后在多出来的位置上补零,比如,对于5<<2,5的二进制为101,左移两位就是10100,那么最后的结果就是20,。
利用这个特点,我们可以通过使用一个特殊的数字1,来解决这个枚举问题。枚举的第一步就是确定到底要改变哪几位数,联想到二进制,我们可以这样处理,就题目的测试数据而言,一行有四个数,用一个二进制数xxxx表示,易知,xxxx一共有2^4种排列,其实也是1<<4,之所以这样写,是因为这比pow要快,所以,我们让k从0开始枚举到15,也就是0000到1111,然后规定,只要是带1的位置,就要翻转这个位置的数字,问题又来了,怎么知道哪一位是1,呢,这里还是用到了二进制,即与运算,我们让k分别与1000,0100,0010,0001进行与运算,分别对应不同的位,如果结果不是1,说明这一位上不是0,是不是灰常巧妙,当然,那四个值依然是通过1的左移来计算出来的,核心的东西讲完了,下面看看代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 16;
int g
, t
, f
;
int cnt, n, m;
int x[4] = {0, 0, -1, 1};
int y[4] = { -1, 1, 0, 0};
void flip(int i, int j)//翻转
{
++cnt, f[i][j] = 1;//步数加1,记录翻转了哪个瓷砖
t[i][j] = !t[i][j];//首先翻转自己
for(int k = 0; k < 4; ++k)//向四个方向寻找,找到就翻转,这里使用了异或
if(i + x[k] > -1 && j + y[k] > -1)
t[i + x[k]][j + y[k]] ^= 1;
}
bool ok(int k)//对于第一行的每一种情况,判断是否能够产生最终的结果
{
cnt = 0;//初始化步数
memcpy(t, g, sizeof(t));//初始化临时数组,作为原始数组的副本
for(int j = 0; j < m; ++j)//这里采用了二进制压缩,例如,j从0到3,那么1 << (m - 1 - j)的二进制就是1000,0100,0010,0001
if(k & (1 << (m - 1 - j)))//对于k的每一个取值,如1010,找到不为0的列,因为只需要翻转1就可以了,用到了与运算
flip(0, j);//如果某一列不为0,就翻转第一行的这个位置
for(int i = 1; i < n; ++i)//当第一行全部翻转完了,原来为1的位置肯定是0,原来是0的位置肯定是1,这就需要第二行来解决这些为1位置,以此类推
for(int j = 0; j < m; ++j)
if(t[i - 1][j]) flip(i, j);//如果该列上一个位置是1,那么这个位置需要翻,否则不需要翻
for(int j = 0; j < m; ++j)//因为每一行的1都可以由下一行搞定,但是最后一行没有下一行,所以只需要考察最后一行最后是不是全0就可以了
if(t[n - 1][j]) return false;
return true;
}
int main()
{
int ans, p;
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i < n; ++i)//数据输入
for(int j = 0; j < m; ++j)
scanf("%d", &g[i][j]);
ans = n * m + 1, p = -1;//初始化
for(int i = 0; i < (1 << m); ++i)//i表示一个二进制数,用来枚举第一行的各种不同翻法,如0001就是只翻最后一个
if(ok(i) && cnt < ans) //如果找到一种可能并且所用的步数更少的话,记下这种翻法
ans = cnt, p = i;
memset(f, 0, sizeof(f));
if(p >= 0)//最后找到的就是最少的翻法,模拟一遍,然后输出
{
ok(p);
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
printf("%d%c", f[i][j], j < m - 1 ? ' ' : '\n');
}
else puts("IMPOSSIBLE");
}
return 0;
}
Fliptile
Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%I64d
& %I64u
SubmitStatusPracticePOJ
3279
Description
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 × 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".
Input
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
Output
Lines 1.. M: Each line contains
N space-separated integers, each specifying how many times to flip that particular location.
Sample Input
4 4 1 0 0 1 0 1 1 0 0 1 1 0 1 0 0 1
Sample Output
0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0
做这题之前,我们首先需要理解一个现象:除了最后一行,其他任何一行上的1都可以通过下一行的翻转转换成0。
也就是说,除了最后一行外,我们总是可以通过翻转,将前n-1行翻成全0。
只要按照这样的原则:对于某一个位置x,如果它的上一个是1,就翻转它,如果是0,就不翻转。
知道了这个,我们应该就能想到,第一行的翻法直接就决定了后面的所有的翻法,这就是我们解决这道题目的思路,即枚举第一行所有可能的翻法,这里,如果用循来枚举,未免太浪费时间了,所以,这里采取了一种二进制压缩的方法。
首先让我们来学习一下左移<<的概念,其实很好理解,就是将一个数转换为二进制,然后向左移动若干位,然后在多出来的位置上补零,比如,对于5<<2,5的二进制为101,左移两位就是10100,那么最后的结果就是20,。
利用这个特点,我们可以通过使用一个特殊的数字1,来解决这个枚举问题。枚举的第一步就是确定到底要改变哪几位数,联想到二进制,我们可以这样处理,就题目的测试数据而言,一行有四个数,用一个二进制数xxxx表示,易知,xxxx一共有2^4种排列,其实也是1<<4,之所以这样写,是因为这比pow要快,所以,我们让k从0开始枚举到15,也就是0000到1111,然后规定,只要是带1的位置,就要翻转这个位置的数字,问题又来了,怎么知道哪一位是1,呢,这里还是用到了二进制,即与运算,我们让k分别与1000,0100,0010,0001进行与运算,分别对应不同的位,如果结果不是1,说明这一位上不是0,是不是灰常巧妙,当然,那四个值依然是通过1的左移来计算出来的,核心的东西讲完了,下面看看代码吧
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 16;
int g
, t
, f
;
int cnt, n, m;
int x[4] = {0, 0, -1, 1};
int y[4] = { -1, 1, 0, 0};
void flip(int i, int j)//翻转
{
++cnt, f[i][j] = 1;//步数加1,记录翻转了哪个瓷砖
t[i][j] = !t[i][j];//首先翻转自己
for(int k = 0; k < 4; ++k)//向四个方向寻找,找到就翻转,这里使用了异或
if(i + x[k] > -1 && j + y[k] > -1)
t[i + x[k]][j + y[k]] ^= 1;
}
bool ok(int k)//对于第一行的每一种情况,判断是否能够产生最终的结果
{
cnt = 0;//初始化步数
memcpy(t, g, sizeof(t));//初始化临时数组,作为原始数组的副本
for(int j = 0; j < m; ++j)//这里采用了二进制压缩,例如,j从0到3,那么1 << (m - 1 - j)的二进制就是1000,0100,0010,0001
if(k & (1 << (m - 1 - j)))//对于k的每一个取值,如1010,找到不为0的列,因为只需要翻转1就可以了,用到了与运算
flip(0, j);//如果某一列不为0,就翻转第一行的这个位置
for(int i = 1; i < n; ++i)//当第一行全部翻转完了,原来为1的位置肯定是0,原来是0的位置肯定是1,这就需要第二行来解决这些为1位置,以此类推
for(int j = 0; j < m; ++j)
if(t[i - 1][j]) flip(i, j);//如果该列上一个位置是1,那么这个位置需要翻,否则不需要翻
for(int j = 0; j < m; ++j)//因为每一行的1都可以由下一行搞定,但是最后一行没有下一行,所以只需要考察最后一行最后是不是全0就可以了
if(t[n - 1][j]) return false;
return true;
}
int main()
{
int ans, p;
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i < n; ++i)//数据输入
for(int j = 0; j < m; ++j)
scanf("%d", &g[i][j]);
ans = n * m + 1, p = -1;//初始化
for(int i = 0; i < (1 << m); ++i)//i表示一个二进制数,用来枚举第一行的各种不同翻法,如0001就是只翻最后一个
if(ok(i) && cnt < ans) //如果找到一种可能并且所用的步数更少的话,记下这种翻法
ans = cnt, p = i;
memset(f, 0, sizeof(f));
if(p >= 0)//最后找到的就是最少的翻法,模拟一遍,然后输出
{
ok(p);
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
printf("%d%c", f[i][j], j < m - 1 ? ' ' : '\n');
}
else puts("IMPOSSIBLE");
}
return 0;
}
相关文章推荐
- 组队赛3解题报告:精度与二进制枚举
- hdu 1172 猜数字 解题报告<暴力枚举>
- 2011.11.25 poj2965 poj1713 poj3295 解题报告【用DFS来枚举】
- hdu 1557 权利指数 ( 二进制枚举子集) 解题心得
- UVa 1382 Distant Galaxy 解题报告(枚举 + 前缀和)
- 一中OJ #1176 立方数分解[尼科梅彻斯定理] | 数论 + 枚举 | 解题报告
- pku 1185 炮兵阵地 压缩dp 解题报告
- POJ3522 Slim Span 解题报告【Kruskal求最小生成树+枚举】
- SDUT 3513 皮卡丘的梦想 (二进制+线段树) -- 解题报告
- UVa 812 - Trade on Verweggistan 解题报告(枚举)
- hdu 1044 Collect More Jewels 解题报告 + 状态压缩分析
- Uva 11210 Chinese Mahjong 解题报告(枚举+码力)
- 状态压缩DP POJ 1699解题报告
- hdu 4774 Stealing Harry Potter's Precious 状态压缩+bfs 解题报告
- UVa 1523 Helicopter 解题报告(枚举排列)
- CF 453B Little Pony and Harmony Chest 解题报告(状态压缩DP)
- 第六届 蓝桥杯 第七题 手链样式 枚举 暴力 解题报告
- 2009 ACM/ICPC 武汉赛区G题解题报告(状态压缩 dp )
- Leetcode 401. Binary Watch 二进制的表 解题报告
- POJ1042解题报告,很简单的贪心枚举