Fliptile (反转模拟或者dfs)

题目来源https://vjudge.net/contest/159739#problem/D
【题意】
给你0 1相间的矩阵砖块,反转之后得到相反的数,但是反转一个砖块,他的上下左右全都会反转,问,能不能把所有的1反转成0,最少需要多少步。
【思路】
首先,这道题我说的会比较详细,因为这道题比较好玩,不仅用到了二进制状态压缩,还用了模拟,或者是dfs。下面是我的叙述:
我们的思路可以是这样的,先把第一行给确定了,然后通过下一行进行反转得到想要的,然后下一行在通过下下一行的反转来确定,就这样一直循环到最后一行,到最后一行的时候,假如最后一行都是0,那说明此时的第一行的状态是可以的,如果有一个不为0,那么就说明这个状态不可取。然后无论可取不可取,都要换一个第一行的状态,在继续,因为要找到最小值。
然后,这一种做法每一个砖块最多只会反转一次(我说的是主动的,不是受影响的),最少是0次;下面解释一下我的代码。
在我的代码里,二进制状态压缩,也就是 int row=1《《m,意思是第一行的 每一种状态,举个例子,0 0 0 1这是一种状态,那么压缩成二进制然后换成十进制就是1,0 1 0 1就是5,那么当m等于4 的时候有多少种状态呢,有2^4种,换成位运算,就变成了左移运算符《《,状态有了,接下来便是一个一个的遍历,那么我的M二维数组是做什么的呢,是用来记录某个位置反转了多少次,而judge函数就是用来判断一下,经过了他自己或者是周围的点反转之后,当前点记录的是1还是0,然后通过这个来判断他同列的下一行那个砖块是否需要反转。而G数组记录的是最优的状态。
【代码】

#include<stack>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
int d[5][2]= {{0,0},{0,1},{0,-1},{1,0},{-1,0}};
int n,m;
int mp[16][16];
int M[16][16];
int G[16][16];
bool judge(int x,int y)
{
    int res=mp[x][y];
    for(int i=0; i<5; i++)
    {
        int rx=x+d[i][0];
        int ry=y+d[i][1];
        if(rx>=0&&rx<n&&ry>=0&&ry<m)
            res+=M[rx][ry];
    }
    return res&1;
}
int check()
{
    for(int i=1; i<n; i++)
        for(int j=0; j<m; j++)
            if(judge(i-1,j)!=0)
                M[i][j]=1;
    for(int i=0; i<m; i++)
        if(judge(n-1,i)!=0)
            return -1;
    int res=0;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            res+=M[i][j];
    return res;

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                scanf("%d",&mp[i][j]);
        int row=1<<m;
        int sum=0,ans=-1;
        for(int i=0; i<row; i++)
        {
            mem(M,0);
            for(int j=0; j<m; j++)
                M[0][m-j-1]=i>>j&1;
            sum=check();
            if(sum>=0&&(ans<0||ans>sum))
            {
                ans=sum;
                memcpy(G,M,sizeof(M));
            }
        }
        if(ans==-1)
            printf("IMPOSSIBLE\n");
        else
        {
            for(int i=0; i<n; i++)
            {
                printf("%d",G[i][0]);
                for(int j=1; j<m; j++)
                {
                    printf(" %d",G[i][j]);
                }
                printf("\n");
            }
        }

    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值