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思路并不难,但是大牛们的代码初见时着实没咋看懂,算是一次代码层次性的训练吧(今天也是和水题难分难解呢

posted @ 2019-09-25 02:57  hanny007  阅读(185)  评论(0编辑  收藏  举报