POJ3279 奶牛开关问题

题目是一道简单的开关问题(插句题外话,记得小时候玩过肯德基制作的一款Flash小游戏,就是根据开关问题来设计的。),要将一个M X N的黑白色相间的格子翻转为白色(翻转可会使指定格子以及其上下左右相邻的格子反色),并求出最优解。

解题思路是先指定第一行格子的翻转方法。并判断下一行与之相邻的格子是否需要翻转(连续翻转两次==不反转)。以此类推。判断最后一行是否全部为白色,如果不是全白则说明无解。

该算法复杂度为O(MN2^n),符合条件。这里注意,7&4=4,而不是返回1,二分DP里是判断是不是为0就行。

// int g=(7>>2&1),f=7&(1<<2);
     // cout<<g<<" "<<f;//1101,1,4
#include <iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
#define maxn 16
int tile[maxn][maxn];//每个位置原来的情况
int flip[maxn][maxn];//每个位置的翻转情况
int opt[maxn][maxn];
//枚举第一行的所有翻转状况
//根据第一行,判断下面每行的翻转情况
//写一个判断1个位置是否为白色的情况,原来的值+翻转的值,为偶数则为0
//最后一行确定完后,前m-1行都确保为白色了,只要判断最后一行是否为白色
int dir[5][2]={{0,0},{-1,0},{0,-1},{1,0},{0,1}};
int M,N;
#define inf 1<<29
int judge(int x,int y)
{
    int sum=tile[x][y];
    int nx,ny;
    for(int i=0;i<5;i++)
    {
        nx=x+dir[i][0];
        ny=y+dir[i][1];
        if(nx>=0&&nx<M&&ny>=0&&ny<N)
            sum+=flip[nx][ny];
    }
    return sum%2;
}
//第一行的情况确定了,统计翻转次数
int cal()
{
    for(int i=1;i<M;i++)
        for(int j=0;j<N;j++)
    {
        if(judge(i-1,j))//如果上面的是黑色,当前位置就要翻转
        flip[i][j]=1;
    }
    //判断最后一行
    for(int j=0;j<N;j++)
    {
        if(judge(M-1,j))
            return -1;
    }
    //统计次数
    int all=0;
    for(int i=0;i<M;i++)
        for(int j=0;j<N;j++)
        all+=flip[i][j];
    return all;
}
//枚举第一行的情况
void solve()
{
    int ans=inf;
    for(int i=0;i<(1<<N);i++)//2的N次
    {
        memset(flip,0,sizeof(flip));
        for(int j=0;j<N;j++)   //如何获得i第j位的值?
        {
            //j=0时,flip[0][N-1]=i&1,等于flip最高位对应i的最低位
        flip[0][N-j-1]=i>>j&1;
        //flip[0][j]=i&(1<<j);
       // int g=(7>>2&1),f=7&(1<<2);
        // cout<<g<<" "<<f;//1101,1,4
        }
        int num=cal();
        if(num>=0&&num<ans)
           {
            memcpy(opt,flip,sizeof(flip));
            ans=num;
           }
    }
    if(ans==inf)
       printf("IMPOSSIBLE\n");
    else
    {
        for(int i=0;i<M;i++)
            for(int j=0;j<N;j++)
             printf("%d%c",opt[i][j],j+1==N?'\n':' ');
    }
}
int main()
{
    
   while(~scanf("%d%d",&M,&N))
  {

   for(int i=0;i<M;i++)
    for(int j=0;j<N;j++)
    scanf("%d",&tile[i][j]);
   solve();
  }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值