poj 3279 翻转问题 题解

首先翻转2次和不翻是一样的所以其实答案也是1个0 1 矩阵(虽然这句话是废话- -)

因为之前看过类似的题(好像是个灯泡而不是砖块来着不过记不太清了),只记得每一行的修改都要由它后一行的操作来决定(这在某种程度上是一种贪心的算法所以我觉得这题不算搜索题硬说也是一道贪心题),其实也很好理解,每行的操作会变动上一行如果你研究本行的话之前排好的就乱了所以我们研究每行的下一行。

在这个思路的指导上,我们很容易能得到第二行到倒数第二行的操作,而到了最后一行,如果不全为0,那很明显没有别的操作了,即“IMPOSSIBLE”,但是,现在的问题就是那第一行呢?它没有上一行,它的操作该怎么执行?  emmm,暴力枚举吧,对于这个第一行,貌似没有更好的办法了 如果有m列的话第一行就有2的m次方种可能性。

按照这个思路莽莽地去写代码好像也没什么问题,但上网看了一圈发现别人的写法更有层次性,于是就先在此借用一下- -(并对比较复杂的地方进行解析),关于我的莽莽写法,日后能ac了再来比较比较。

在这里引用https://www.cnblogs.com/sky-stars/p/11195560.html大神的代码(如有侵权联系我立删)

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#define mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
#define MEM(x,y) memset(x,y,sizeof(x))
#define Maxn 20
using namespace std;
int result;
int n,m;
int mp[Maxn][Maxn];//原始地图
int tmp[Maxn][Maxn];//当前操作的地图
int ans[Maxn][Maxn];//最优解地图
int dt[][2] = { { -1, 0 }, { 1, 0 }, { 0, 0 }, { 0, -1 }, { 0, 1 } };//5个方向
int getcolor(int x,int y)//得到(x,y)的颜色
{
    int cnt=mp[x][y];
    for(int i=0; i<5; i++)
    {
        int tx=dt[i][0]+x;
        int ty=dt[i][1]+y;
        if(tx>=0&&tx<n&&ty>=0&&ty<m)
            cnt+=tmp[tx][ty];
    }
    return cnt%2;
}
int solve()
{
    int cnt=0;
    for(int i=1; i<n; i++)//从第二行开始检查是否需要翻转
        for(int j=0; j<m; j++)
            if(getcolor(i-1,j))
                tmp[i][j]=1;
    for(int i=0; i<m; i++)//检查最后一行是否全为0
        if(getcolor(n-1,i))
            return INF;
    for(int i=0; i<n; i++)//统计翻转次数
        for(int j=0; j<m; j++)
            cnt+=tmp[i][j];
    return cnt;
}
int main()
{
    result=INF;
    cin>>n>>m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin>>mp[i][j];
    for(int i=0; i<1<<m; i++)//按照字典序枚举第一行所以翻转可能
    {
        MEM(tmp,0);//初始化地图
        for(int j=0; j<m; j++)
            tmp[0][j]= i>>(m-(j+1)) & 1;//第一行的状态
        int sum=solve();//翻转的次数
        if(sum<result)//更新地图
        {
            result=sum;
            memcpy(ans,tmp,sizeof tmp);//tmp数组复制到ans
        }
    }
    if(result==INF)
        cout<<"IMPOSSIBLE"<<endl;
    else//输出
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
                cout<<ans[i][j]<<" ";
            cout<<endl;
        }
    }
}

那么对于这份代码结合我们上面讲的思路,我来说几个我在阅读时有所思考的地方。

1.它采用了一个中间tmp数组来记录每个砖块的翻转次数,是为了寻求翻转次数最少,搭了个中间桥

2.关于第一行的枚举:

用二进制的位移去枚举只有01的数列很巧妙(注意它枚举的是翻转次数不是颜色)

3:关于getcolor

这个代码最巧的就是getcolor

对于每个砖块有5种反转的方式,被周围四个带动和他自己,并且它的颜色等于他自己的颜色翻转次数加和余2,我觉得这个说难想确实不会往加和还有余数上去想,但是确实还蛮好理解

4:本题关键核心是怎么解决这个字典序的问题

这就是day3的内容啦,一道贪心(还有状态压缩?)的题目,哦因为poj3278太简单了所以这是专题一的第四道题。题吗,emm思路并不难,但是大牛们的代码初见时着实没咋看懂,算是一次代码层次性的训练吧(今天也是和水题难分难解呢)

原文地址:https://www.cnblogs.com/hanny007/p/11582155.html

时间: 2024-08-30 18:07:21

poj 3279 翻转问题 题解的相关文章

POJ 3279(Fliptile)题解

以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定长宽的黑白棋棋盘摆满棋子,每次操作可以反转一个位置和其上下左右共五个位置的棋子的颜色,求要使用最少翻转次数将所有棋子反转为黑色所需翻转的是哪些棋子(这句话好长...). [题目分析] 盯着奇怪的题目看了半天发现和奶牛没什么卵关系..奶牛智商高了产奶高是什么鬼说法... 这题刚开始被放到搜索的分类下了..然而这和搜索有什么关系..没经验所以想了各种和搜索沾边的方法,结果没想出解法,直到看了网上的题解,压根不是搜索啊有木有.

状态压缩+枚举 POJ 3279 Fliptile

题目传送门 1 /* 2 题意:问最少翻转几次使得棋子都变白,输出翻转的位置 3 状态压缩+枚举:和之前UVA_11464差不多,枚举第一行,可以从上一行的状态知道当前是否必须翻转 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 using namespace std; 9 10 const int MAXN = 20; 11 const int INF = 0x3f3f3f3

POJ 3420 Quad Tiling 题解 《挑战程序设计竞赛》

POJ 3420 Quad Tiling贴瓷砖:4*N的地板上用2*1的瓷砖铺满,求所有方案数对M求余.3.4熟练掌握动态规划矩阵的幂久违地上了节课,太无聊,只好刷一题.假设S[n]表示填满n时的方案数,有S[0]=1.定义矩阵M[p][q] := 边缘p和边缘q可以拼合时取1,否则取0所谓的可以拼合表示,两个边缘拼起来后长度为1(为2就拼接不起来了),且内部缝隙可以用2*1的瓷砖填满.那么M就有一些简单的性质了,比如M的第一行应该是:0 0 0 0 0 0... 继续阅读:码农场 » POJ

POJ 1019 Number Sequence 题解

这又是一道看似简单,实际挺困难的题目. 本来想做道基础题消遣一下的,没想到反被消遣了-_-|||. 看个人的基础吧,对于数学好的会简单点,但是由于情况太多,需要都考虑全,故此难度应该在4星以上了. 我这里使用的方法就是直接打表,然后直接模拟,利用打表去掉一大段数据,剩下数据量十分小了,故此可以直接模拟. 打表是为了计算前面的周期数,把周期数直接去掉. 主要难点是后面10位数以上的数有2位, 3位,4位等情况要考虑.- 下面使用getNewNums一个函数解决了,想通了,就几行代码,还不用难理解的

POJ 3982 序列 大数题解

又是一道大数相加的题目,直接模板或者Java都可以水过了. 循环相加33次就可以了,计算出A99是第几个,准确输出答案. #include <stdio.h> #include <string> #include <algorithm> using std::string; const int MAX_B = 5120; char buf[MAX_B]; int id = 0, len = 0; inline char getFromBuf() { if (id >

POJ 3411 Paid Roads 题解 《挑战程序设计竞赛》

POJ 3411 Paid Roads开路:N个城市间有m条单向路,分别从a到b,可以在c处交P路费,也可以直接交R路费.那么问题来了,你的挖掘机怎么开最省钱?3.4熟练掌握动态规划状态压缩DP乍一看可以Dijkstra,实际上的确可以Dijkstra.不过多了一个预交费的c,所以在遍历的时候多了一维状态,这个维度储存当前走过的城市集合.在Dijkstra的时候,如果走过了c那么就有两个选择,选其中最省的即可:否则没得选.#include <iostream> #include&nb.

POJ 2499 Binary Tree 题解

本题使用所谓的辗转相除法. 还需要逆过来遍历二叉树.可以想象给出的数据点是根节点,然后遍历到根节点(1,1). 考的是根据给出的规则,总结规律的能力. #include <stdio.h> namespace BinaryTree2499_1 { int main() { int T, a, b, le, ri; scanf("%d", &T); for (int t = 1; t <= T; t++) { scanf("%d %d", &

POJ 1018 Communication System 题解

本题一看似乎是递归回溯剪枝的方法,我一提交,结果超时. 然后又好像是使用DP,还可能我剪枝不够. 想了很久,无奈忍不住偷看了下提示,发现方法真多,有贪心,DP,有高级剪枝的,还有三分法的,八仙过海各显神通啊. 坏习惯了,没思考够深入就偷看提示了. 幸好及时回头,还不需要看别人的代码了.自己做出来之后,有空看看多种解法的代码也好. 然后我想出自己的思路了,使用贪心,剪枝,DP综合优化下,呵呵,最后程序有点复杂,优化到了16ms,运气好点,或者vector换成原始数组的话,应该可以0MS了. 总体思

POJ 2823 Sliding Window 题解

POJ 2823 Sliding  Window 题解 Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the slidi