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

 

  

 

posted @ 2019-02-27 23:04  liuyongliu  阅读(185)  评论(0)    收藏  举报