POJ3279

黑白翻转的问题,值得借鉴的是:

1.使用左移+按位与运算的方法保留二进制相应位置的数字

2.memcpy函数的使用,用于数组的复制

3.使用0x3f3f3f3f表示无穷大 

#include<iostream>
#include<cstring>
#include<string>//数组复制函数
using namespace std;
int mapp[20][20];//初始数组
int cal[20][20];//每个位置是否翻转的数组
int opt[20][20];//最终结果
int n,m;//n行m列
int dir[5][2]= {{0,0},{0,1},{0,-1},{1,0},{-1,0}};
bool judge(int x,int y)
{
    if(x<1||x>n||y<1||y>m)
        return false;
    else
        return true;
}
int func(int x,int y)//判断是否为黑色,是返回1
{
    //(x,y)位置的状态由初始状态+它本身+周围4个位置的翻转状态得到
    int res=mapp[x][y];
    for(int i=0; i<5; i++)
    {
        int xx=x+dir[i][0];
        int yy=y+dir[i][1];
        if(judge(xx,yy))//判断位置是否合法
        {
            res+=cal[xx][yy];
        }
    }
    return res%2;
}
int dfs()
{
    for(int i=2; i<=n; i++)
        for(int j=1; j<=m; j++)
            if(func(i-1,j))//如果(i-1,j)位置为黑色,cal[i][j]必须翻转
                cal[i][j]=1;
//按照这个规则翻转到最后一行,检查最后一行是不是都为白色
    for(int i=1; i<=m; i++)
        if(func(n,i))//一旦出现黑色的位置
            return -1;//翻转失败
    int res=0;//记录结果次数
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            res+=cal[i][j];
    return res;
}
int main()
{
    //这种对行数和列数要求高的矩阵,直接用i表示行,j表示列
    cin>>n>>m;
    int ans=0x3f3f3f3f;//大体表示无穷,2^9
    int flag=0;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            cin>>mapp[i][j];
//枚举第一行翻转和不翻转的所有情况
    for(int i=0; i<1<<m; i++) //i表示的时1到(2^m-1)这m个数字
    {
        memset(cal,0,sizeof(cal));//枚举2^m种情况,寻找最优解
        for(int j=1; j<=m; j++)
        {
            cal[1][j]=i>>(m-j)&1;
        }//(1,j)坐标位置记录i相应位置的二进制数字
//这个方法值得借鉴,用【左移之后按位与1】的方法记录相应位置的二进制数字

        int temp=dfs();
        if(temp>=0&&temp<ans)//最小次数
        {
            ans=temp;
            memcpy(opt,cal,sizeof(cal));//数组复制函数
            flag=1;
        }
    }
    if(flag)
    {
        for(int i=1; i<=n; i++)
        {

            for(int j=1; j<=m; j++)
            {
                if(j!=1)
                    cout<<" ";
                cout<<opt[i][j];
            }
            cout<<endl;
        }
    }
    else
        cout<<"IMPOSSIBLE"<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值