zoukankan      html  css  js  c++  java
  • Fliptile奶牛踩瓷砖 (状态压缩,开关问题,枚举)

    题目:Fliptile

    题意:

    给定一个M*N矩阵,有些是黑色(1表示)否则白色(0表示),每翻转一个(i,j),会使得它和它周围4个格变为另一个颜色,要求翻转最少的点,使得变为全白色的矩阵,输出这个标记了翻转点的矩阵,如果有多个最优解,输出逆字典序最小的那个矩阵,若没有解,输出IMPOSSIBLE。

    题解:

    解法一:(对第一行的踩法进行DFS搜索)

    #include <iostream>
    #include <cstring>
    #include <queue>
    #include <cmath>
    using namespace std;
    #define ll long long
    const int inf=0x3f3f3f3f;
    const ll inff=0x3f3f3f3f3f3f3f3f;
    
    int m,n;
    int a[20][20];//每一遍DFS后的初始状态(即第一行踩过后进入solve前的状态) 
    int ans[20][20];//记录DFS下第一行的踩法,这里二维数组多余了... 
    int pace=inf;
    int ann[20][20];//最终的踩法 
    
    void cai(int j)//踩第一行第j个 
    {
        ans[1][j]=(ans[1][j]==1? 0:1);
        if(j>1)
        a[1][j-1]=(a[1][j-1]==1? 0:1);
        a[1][j]=(a[1][j]==1? 0:1);
        if(j<n)
        a[1][j+1]=(a[1][j+1]==1? 0:1);
        if(m>=2)
        a[2][j]=(a[2][j]==1? 0:1);
    }
    
    void solve(int pa)
    {
        int a1[20][20];
        int ans1[20][20];//记录此例下的结果 
        
        for(int i=1;i<20;i++)
        for(int j=1;j<20;j++) ans1[i][j]=ans[i][j],a1[i][j]=a[i][j];
        
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(a1[i][j]){
                    ans1[i+1][j]=1;
                    pa++;
                    if(j>1)
                    a1[i+1][j-1]=(a1[i+1][j-1]==1? 0:1);
                    a1[i+1][j]=(a1[i+1][j]==1? 0:1);
                    if(j<n)
                    a1[i+1][j+1]=(a1[i+1][j+1]==1? 0:1);
                    
                    a1[i][j]=(a1[i][j]==1? 0:1);
                    if(i+2<=m)
                    a1[i+2][j]=(a1[i+2][j]==1? 0:1);
                }
            }
        }
        for(int j=1;j<=n;j++)//最后一行判断,若没有1,则成功 
        {
            if(a1[m][j]) {
                return ;
            }
        }
        if(pa<pace){//记录最小踩法 
            pace=pa;
            for(int i=1;i<20;i++)
            for(int j=1;j<20;j++) ann[i][j]=ans1[i][j];
        }
    }
    
    void DFS(int x,int pa)//第一行第x个踩还是不踩 
    {//pa为此次DFS决定踩不踩之前,第一行已经踩过的步数 
        cai(x);//
        solve(pa+1);
        
        if(x<n)
        DFS(x+1,pa+1);
        
        cai(x);//不踩(再踩一次相当于不踩) 
        solve(pa);
        
        if(x<n)
        DFS(x+1,pa);
    }
    
    void print()
    {
        if(pace==inf){
            cout<<"IMPOSSIBLE"<<endl;
            return ;
        }
        //cout<<pace<<endl;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cout<<ann[i][j];
                if(j!=n) cout<<" ";
            }
            cout<<endl;
        }
    }
    
    int main()
    {
        cin>>m>>n;
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++) cin>>a[i][j];
        DFS(1,0);
        
        print(); 
        return 0;
    }
    View Code

    参考:Fliptile POJ3279 二进制压缩枚举 解题报告

    只要第一行的方案确定,后面的踩发就能确定,所以状压枚举第一行的方案

    代码:

    /***********************************************/
    int ans[30][30];
    int a[34][34];
    int b[33][33];
    int m,n;
    int ANS=inf;
    ll daan=-1;
    
    int fanzhuan1(ll Y)
    {
        mem0(ans);
        int pp=0;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++) a[i][j]=b[i][j];
        for(int i=0;i<n;i++)
        {
            if(Y&(1ll<<i)) //翻转第一行第n-i个
            {
                pp++;
                ans[1][n-i]=1;
                a[1][n-i]=a[1][n-i]?0:1;
                if(i>0) a[1][n-i+1]=a[1][n-i+1]?0:1;
                if(i<n-1) a[1][n-i-1]=a[1][n-i-1]?0:1;
                if(n>1) a[2][n-i]=a[2][n-i]?0:1;
            }
        }
    
        return pp;
    }
    
    void solve(int pp,ll p1)
    {
    
        for(int i=1;i<m;i++)
        {
            for(int j=n;j>=1;j--)
            {
                if(a[i][j])
                {
                    ans[i+1][j]=1;
                    a[i][j]=0;
                    a[i+1][j]=a[i+1][j]?0:1;
                    if(j>1) a[i+1][j-1]=a[i+1][j-1]?0:1;
                    if(j<n) a[i+1][j+1]=a[i+1][j+1]?0:1;
                    if(i+2<=m) a[i+2][j]=a[i+2][j]?0:1;
                }
            }
        }
        for(int j=1;j<=n;j++)
        {
            if(a[m][j]){
                //cout<<"IMPOSSIBLE"<<endl;
                return ;
            }
        }
        ///取最优
        int sum=pp;
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            if(ans[i][j]) sum++;
        }
        if(sum<ANS)
        {
            ANS=sum;
            daan=p1;
        }
    }
    
    int main()
    {
        std::ios::sync_with_stdio(false);
        std::cin.tie(0);
        cin>>m>>n;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++) cin>>b[i][j];
    
        int Y=(1<<n)-1;
        for(ll i=0;i<=Y;i++)
        {
            int t=fanzhuan1(i);
            //cout<<t<<endl;
            solve(t,i);
        }
        if(daan==-1) cout<<"IMPOSSIBLE"<<endl;
        else
        {
            //cout<<daan<<endl;
            int t=fanzhuan1(daan);
            solve(t,daan);
            for(int i=1;i<=m;i++)
            {
                for(int j=1;j<n;j++) cout<<ans[i][j]<<" ";
                cout<<ans[i][n]<<endl;
            }
        }
    
    
        return 0;
    }
    View Code

      

  • 相关阅读:
    使用vscode 用git 拉取代码,提示:在签出前,请清理存储库工作树
    区分手机端和pc端加载不同的样式
    关于vue的代码规范
    各种名词汇总整理
    ZB埋点汇总
    项目实战 OLAP数据提取
    大数据intern_1总结:数据埋点以及SQL复习
    leetcode 343+279+91+64+70 动态规划
    leetcode 241 加优先级括号
    leetcode 17+79+93+46+47+77+39+40+78+90+131+37 回溯法
  • 原文地址:https://www.cnblogs.com/liuyongliu/p/10447698.html
Copyright © 2011-2022 走看看