【POJ 3279 Fliptile】开关问题,模拟
2016-05-28 22:42
302 查看
题目链接:http://poj.org/problem?id=3279
题意:给定一个n*m的坐标方格,每个位置为黑色或白色。现有如下翻转规则:每翻转一个位置的颜色,与其四连通的位置都会被翻转,但注意只扩散一圈,不是连锁反应。
求最少翻转几个位置能够使所有n*m个位置都变为白色。若有解,求字典序最小的翻转方案(给出每个位置的翻转次数)。
数据范围:n, m 属于 [1, 15]
思路:我们把翻转方案中的翻转称为“主动翻转”,翻转过程中受邻居影响而发生的翻转称为“被动翻转”。观察例子可得出如下几个结论:
1. 最优解中,每个位置的主动翻转不超过1次。
2. 各个位置翻转的次序对结果没有影响。
3. 某一时刻,位置(i, j)的颜色取决于到目前为止它的主动翻转和被动翻转次数之和,记为cnt;具体地,由结论1可推得cnt为奇数则变色,偶数则不变色。
4. 位置(i, j)的被动翻转次数 = 与它四连通的位置的主动翻转次数之和。
解法:首先枚举第一行的所有可能的翻转动作,这里由于m <= 15,所以用一个整型实现“状态压缩”,即枚举所有 i 属于[0, 2m-1], i 的二进制展开的第 j 位代表原图第一行第 j 个位置是否主动翻转。
然后,由结论2、3,可对每个第一行的枚举值从第二行开始自上而下逐行递推:第 i-1 行通过第 i 行垂直位置的主动翻转而“修正”;具体地,对于位置(i, j),考查位置(i-1, j),若(i-1, j)为黑色,则目前一定要对(i, j)主动翻转才能带动(i-1, j)被动翻转为白色。这样的修正持续到最后一行,由于没有下一行可以修正最后一行,因此可直接判最后一行是否为全白,若不是则无解。
最后,对于最优解的维护,这里用二维数组b直接记录每个位置进行的主动翻转的次数。由结论3、4,可以通过数组b在O(1)时间内算出当前某个位置的颜色,以便判断是否需要修正。
解法和代码参照了http://blog.csdn.net/ac_hell/article/details/51077271 以及 http://blog.csdn.net/ac_hell/article/details/51077320,写得很清楚,学习了。
本来是做今天的计蒜之道的热身赛A题,与这道同类型。比赛时不会,然而比赛结束后已不能提交。。。
题意:给定一个n*m的坐标方格,每个位置为黑色或白色。现有如下翻转规则:每翻转一个位置的颜色,与其四连通的位置都会被翻转,但注意只扩散一圈,不是连锁反应。
求最少翻转几个位置能够使所有n*m个位置都变为白色。若有解,求字典序最小的翻转方案(给出每个位置的翻转次数)。
数据范围:n, m 属于 [1, 15]
思路:我们把翻转方案中的翻转称为“主动翻转”,翻转过程中受邻居影响而发生的翻转称为“被动翻转”。观察例子可得出如下几个结论:
1. 最优解中,每个位置的主动翻转不超过1次。
2. 各个位置翻转的次序对结果没有影响。
3. 某一时刻,位置(i, j)的颜色取决于到目前为止它的主动翻转和被动翻转次数之和,记为cnt;具体地,由结论1可推得cnt为奇数则变色,偶数则不变色。
4. 位置(i, j)的被动翻转次数 = 与它四连通的位置的主动翻转次数之和。
解法:首先枚举第一行的所有可能的翻转动作,这里由于m <= 15,所以用一个整型实现“状态压缩”,即枚举所有 i 属于[0, 2m-1], i 的二进制展开的第 j 位代表原图第一行第 j 个位置是否主动翻转。
然后,由结论2、3,可对每个第一行的枚举值从第二行开始自上而下逐行递推:第 i-1 行通过第 i 行垂直位置的主动翻转而“修正”;具体地,对于位置(i, j),考查位置(i-1, j),若(i-1, j)为黑色,则目前一定要对(i, j)主动翻转才能带动(i-1, j)被动翻转为白色。这样的修正持续到最后一行,由于没有下一行可以修正最后一行,因此可直接判最后一行是否为全白,若不是则无解。
最后,对于最优解的维护,这里用二维数组b直接记录每个位置进行的主动翻转的次数。由结论3、4,可以通过数组b在O(1)时间内算出当前某个位置的颜色,以便判断是否需要修正。
解法和代码参照了http://blog.csdn.net/ac_hell/article/details/51077271 以及 http://blog.csdn.net/ac_hell/article/details/51077320,写得很清楚,学习了。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int MAX_N = 16; const int MAX_M = 16; const int INF = 0x7fffffff; int a[MAX_N][MAX_M];//原图 int b[MAX_N][MAX_M];//此位置共翻转了几次 int c[MAX_N][MAX_M];//最优解 int n, m; int dx[] = {0, 0, 0, 1, -1}, dy[] = {1, -1, 0, 0, 0}; bool out(int x, int y){ if(x < 0 || x >=n || y < 0 || y >= m) return true; return false; } int get_color(int x, int y){ int color = a[x][y]; for(int i=0; i<5; i++){ int nx = x + dx[i], ny = y + dy[i]; if(out(nx, ny)) continue; color += b[nx][ny];//主动+被动 } return color & 1; } int flip(int s){ for(int i=1; i<=m; i++){ b[0][i-1] = (s>>(m-i)) & 1; //printf("%d ", b[0][i-1]); } for(int i=1; i<n; i++){ for(int j=0; j<m; j++){//(i-1, j)想变,必须让(i, j)翻转 if(get_color(i-1, j)){ b[i][j] = 1; //printf("%d, %d\n", i, j); } } } for(int i=0; i<m; i++){ if(get_color(n-1, i)){ //printf("%d no\n", i); return INF; } } int times = 0; for(int i=0; i<n; i++){ for(int j=0; j<m; j++){ times += b[i][j]; } } return times; } int main() { freopen("3279.txt", "r", stdin); scanf("%d%d", &n, &m); for(int i=0; i<n; i++){ for(int j=0; j<m; j++){ scanf("%d", &a[i][j]); } } int ans = INF; for(int i=0; i< (1<<m); i++){//信号量集 memset(b, 0, sizeof(b));//翻转方案 int t = flip(i); if(t < ans){ ans = t; memcpy(c, b, sizeof(b));//更新最优解 } } if(ans == INF) printf("IMPOSSIBLE\n"); else{ for(int i=0; i<n; i++){ for(int j=0; j<m; j++){ printf("%d ", c[i][j]); } printf("\n"); } } return 0; }
本来是做今天的计蒜之道的热身赛A题,与这道同类型。比赛时不会,然而比赛结束后已不能提交。。。
相关文章推荐
- 超详细的 linux挂载详解
- jdk annotation发布webservice服务 及生成客户端调用代码
- DFS-GirlCat
- [javaSE] 标识符大小写
- c++实验6
- shell变量和环境变量
- C++删除文本文件空行
- Spring Boot中使用Spring Security进行安全控制
- C++第6次实验-数组操作
- 关于Eclipse的编码配置和字体大小设置
- java太low,又舍不得jvm平台的丰富资源?试试kotlin吧(一)
- 残酷的竞争
- 腾讯2017暑期实习生编程题
- Nginx中运行403 forbidden错误
- Java中RMI的实现
- 堆栈和队列知识总结
- 递归方法实现树形数据
- java设计模式之策略模式
- 小代码 html 移动div 图片 文字 任意性
- list, tuple, dict, set的用法总结