acm专题学习之搜索(一)翻转问题+POJ - 3279

题目:给一个n*m的矩阵,全部都是由0和1构成的,每次翻转一个点的时候周围上下左右的点也会翻转。现在可以选择点进行翻转,输出每个点翻转次数的矩阵,翻转总次数要最小,如果有多个这样的矩阵输出最小字典序的。

条件:1 每次翻转一个点的时候周围上下左右的点也会翻转

            2 如果有多个这样的矩阵输出最小字典序的

翻转问题:这一类问题一般每个位置都只有两种状态。这类问题一般有三个特点:

 1 某个位置翻转次数n的效果和n%2的效果是一样,所以一个位置要么翻要么不翻

 2 翻转的顺序并不影响结果

 3 从第一个位置开始考虑翻转情况,再每次在上一个情况不变的情况下考虑翻转(搜索)

本题思路:如果是要搜索n*m的情况的话,那么2的15*15次方必定会超时,但是如果只枚举第一行2的15次方,然后通过在第一行依次确定剩下的行数的翻转情况。比如某个矩阵第一行枚举完翻转次数后,第三列的数字还是1,那么假定第一行翻转次数不变,要想改变第一行第三列的数字就必须让第二行第三列的数字翻转。最后再检查最后一行是否全部为0。

还使用了状态压缩,枚举第一行的时候用了二进制数,通过第一行按字典序枚举,达到字典序小的优先。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
const int maxn=16;
const int inf=0x3f3f3f3f;
int n,m,count1=0,min1=1<<30;//最小翻转次数;
int bo[maxn][maxn],cur[maxn][maxn];//原矩阵,操作后的矩阵
int op[maxn][maxn],ans[maxn][maxn];//操作次数矩阵和答案矩阵
void turn(int x,int y)//翻转
{
    cur[x][y]=(cur[x][y]+1)%2;
    cur[x+1][y]=(cur[x+1][y]+1)%2;
    cur[x-1][y]=(cur[x-1][y]+1)%2;
    cur[x][y+1]=(cur[x][y+1]+1)%2;
    cur[x][y-1]=(cur[x][y-1]+1)%2;
}
int solve()
{
    memcpy(cur,bo,sizeof(bo));//复制原来的矩阵
    for(int i=0;i<m;i++)//根据枚举改变第二行的
    {
        if(op[0][i])//如果第一个没有达成,就反转
        {
            turn(0,i);
            count1++;//步数++
        }
    }
    for(int i=1;i<n;i++)//从第二行开始
    {
        for(int j=0;j<m;j++)
        {
            if(cur[i-1][j])
            {
                op[i][j]=1;
                turn(i,j);
                count1++;//翻转次数++
            }
        }
    }
    for(int i=0;i<m;i++)
    {
        if(cur[n-1][i])
            return 0;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&m);//输入行列
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            scanf("%d",&bo[i][j]);//输入矩阵
        }
    }
    for(int i=0;i<(1<<m);i++)//2的15次方可能,第一行,状态压缩
    {
        memset(op,0,sizeof(op));
        count1=0;
        for(int j=0;j<m;j++)
        {
            op[0][m-j-1]=i>>j&1;//2的15次方每个数字的,二进制每一个位数,保证了枚举从字典序小的开始
        }
        solve();
        if(count1>0&&count1<min1)//翻转总次数最小
        {
            min1=count1;
            memcpy(ans,op,sizeof(op));//答案矩阵复制
        }
    }
    if(min1!=(1<<30))
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                printf("%d ",ans[i][j]);
            }
            printf("\n");
        }
    }
    else
        printf("IMPOSSIBLE\n");
    return 0;
}

总结:1 翻转问题是我以前一直都没有想明白,这次终于把它解决了

            2 终于又重新开始acm做题了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值