P1985 [USACO07OPEN]翻转棋 Fliptile S

95. 费解的开关同题。

暴力总共有\(2^{NM}\)种翻转的方法。

先确定第一行的翻转方式,然后可以很容易判断这样是否存在解以及解的最小步数是多少,最上面一行的翻转方式共有\(O(2^M)\)种,复杂度为\(O(NM2^M)\)

const int N=20;
bool g[N][N],tmp[N][N];
int flip[N][N],ans[N][N];
int n,m;

bool check(int x,int y)
{
return x>=0 && x<n && y>=0 && y<m;
}

void change(int x,int y)
{
flip[x][y]=1;
for(int i=0;i<5;i++)
{
int a=x+dx[i],b=y+dy[i];
if(check(a,b))
tmp[a][b]^=1;
}
}

int main()
{
cin>>n>>m;

for(int i=0;i<n;i++)
    for(int j=0;j<m;j++)
        cin>>g[i][j];

int res=INF;
for(int s=0;s<(1<<m);s++)
{
    memcpy(tmp,g,sizeof g);
    memset(flip,0,sizeof flip);

    int cnt=0;
    for(int i=0;i<m;i++)
        if(s>>i & 1)
            change(0,i),cnt++;

    for(int i=0;i<n-1;i++)
        for(int j=0;j<m;j++)
            if(tmp[i][j])
                change(i+1,j),cnt++;

    bool ok=true;
    for(int i=0;i<m;i++)
        if(tmp[n-1][i])
        {
            ok=false;
            break;
        }

    if(ok && cnt < res)
    {
        res=cnt;
        memcpy(ans,flip,sizeof flip);
    }
}

if(res == INF) puts("IMPOSSIBLE");
else
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
            cout<<ans[i][j]<<' ';
        cout<<endl;
    }
}
//system("pause");
return 0;

}

posted @ 2021-03-20 10:46  Dazzling!  阅读(46)  评论(0编辑  收藏  举报