poj 3279

传送门:http://poj.org/problem?id=3279

题面描述:

给你一个方格,这个方格中有M*N个小方块,每个小方块都是0或1,定义一种反转操作:,每次反转范围是中心方块以及其上下左右的方块,反转后状态变化为0->1,1->0。问能不能通过有限次反转操作,将方格的最终状态变为全是0,如果可以,输出一个反转次数最少而且字典序最小的矩阵,矩阵对应的数值为该位置的反转次数,否则,输出IMPOSSIBLE。

思路:

1.对于同一个位置来说,最多反转1次,因为只有两种状态,反转两次事实上跟原先状态是一样的。

2.对于同一个位置,它的最终状态会受其上下左右各个方块反转的影响,这会使问题的分析变得非常复杂,但是我们可以先确定第一行的反转次序,此时对于第一行来说,能够影响它的左中右的反转都已经确定了,那么决定他最终状态的就只有它的下的反转,调节他下面方块的反转次序,使第一行的状态全变成0,然后对于第二行一样,以此类推,到了倒数第一行,此时不仅确定了倒数第二行的状态,也把最后一行的反转次数确定了,同时,最后一行的状态也被确定了(可能有点绕),此时我们只要判断最后一行是不是全是0就可判断这种第一行的反转次序是不是可以满足条件。

3.接下来,我们来思考如何来枚举第一行的各种反转次数,由1可以知道,每个位置事实上只有0和1两种状态,所以我们可以考虑二进制枚举来解决问题,具体实现在AC代码里面。因为我们是从小到大枚举的,所以最终得到的结果一定是最小字典序的,所以不用再考虑字典序的问题了。

最终代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 20;
int map[maxn][maxn],flip[maxn][maxn],ans[maxn][maxn],m,n;
int dx[]  = {1,0,0,0,-1};
int dy[] = {0,1,0,-1,0};
int get(int x,int y){
    int c =map[x][y];
    for(int d = 0;d<5;d++){
        int x1 = x+dx[d];
        int y1 = y+dy[d];
        if(x1>=0&&x<m&&y>=0&&y<n){
            c+=flip[x1][y1];
        }
    }
    return c%2;
}
int calc(){
    int res = 0;
    for(int i = 1;i<m;i++)
        for(int j = 0;j<n;j++)
        {
            if(get(i-1,j))
            {
                flip[i][j] = 1;
            }
        }
    for(int i = 0;i<n;i++)
        if(get(m-1,i)!=0)
            return -1;
    for(int i = 0;i<m;i++)
        for(int j = 0;j<n;j++)
            res+=flip[i][j];
    return res;
}
int main()
{
    while(~scanf("%d %d",&m,&n)){
        for(int i = 0;i<m;i++)
            for(int j = 0;j<n;j++)
                scanf("%d",&map[i][j]);int res = -1;
        for(int i = 0;i< 1<< n;i++){
            
            memset(flip,0,sizeof(flip));
            for(int j = 0;j<n;j++)
                flip[0][n-j-1] =  i>>j&1;
            int num = calc();
            if(num>=0&&(res<0||res>num)){
                res = num;
                memcpy(ans,flip,sizeof(flip));
            }
        }
        if(res==-1)
            printf("IMPOSSIBLE\n");
        else
        {
            for(int i = 0;i<m;i++)
                for(int j = 0;j<n;j++)
                    printf("%d%c",ans[i][j],j+1==n?'\n':' ');
        }
    }
    return 0;
}

如还有困惑的地方,请在评论区留言交流!

posted @ 2019-06-02 21:22  浅花迷人  阅读(152)  评论(0编辑  收藏  举报