天天看点

【JZOJ5056】【GDSOI2017模拟4.13】黑白广场

Description

东转盘有一个黑白广场,那是帕斯卡金最喜欢的地方。

传说中,这个一望无际的广场是个N行M列的网格图,每个格子都有黑白中的一种颜色。帕斯卡金有一根纤细的魔杖,他可以选择一个格子(i,j),并且可以施加他仅有的两种魔法(任意次数,也可以不操作):

1. 翻转(i,j)的颜色,以及翻转相邻格子的颜色

2. 翻转相邻格子的颜色(翻转:黑色变成白色,白色变成黑色;相邻:两个格子有公共边)

当然了,帕斯卡金魔力有限。你愿意告诉披荆斩棘、扫荡群魔的帕斯卡金如何施加最少次数的魔法让黑白广场变成纯白广场吗。

Data Constraint

10%的数据满足:N*M<=12

30%的数据满足:N*M<=81

100%的数据满足:N,M<=10

Solution

这题是轮廓线dp。我们设出状态f[i][j][s]表示当前做第i行第j列,包括(i,j)的前m个点的3进制状态为s,再前面的均翻为0的最小步数。3进制状态中0表示没翻过且当前为0,1表示没翻过且当前为1,2表示在该点上进行了1次操作。由于一个点假如操作了且当前为1,我们一定可以改变方案,在步数不变的情况下使之变为0。然后我们当前做到(i,j+1),我们可以根据(i,j)和(i-1,j+1)的状态推断出(i,j+1)的值,接下来有几种转移:

1、假如当前(i-1,j+1)为1,我们一定要在(i,j+1)进行操作,才能保证(i-1,j+1)改为0。

2、假如当前(i-1,j+1)为0,我们可以有2种转移:

1:当前的点(i,j+1)毛都不干
2:当前点(i,j+1)采用操作1和操作2

3、假如当前(i-1,j+1)为2,除为0的两种操作外,我们可以转移:采用操作。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=59050,maxn1=11;
int f[maxn1][maxn1][maxn],a[maxn1][maxn1],er[maxn];
int n,m,i,t,j,k,l,x,y,z,p,q,ans;
char s[maxn];
int main(){
    freopen("square.in","r",stdin);freopen("square.out","w",stdout);
    while (1){
    scanf("%d%d\n",&n,&m);
    if (!n) return 0;
    for (i=1;i<=n;i++){
        scanf("%s\n",s+1);
        for (j=1;j<=m;j++)
            a[i][j]=s[j]-48;
    }er[1]=1;
    for (i=2;i<=m+1;i++)
        er[i]=er[i-1]*3;
    p=er[m+1];
    memset(f,127,sizeof(f));f[1][1][(p-1)/3*3+a[1][1]]=0;f[1][1][(p-1)/3*3+1-a[1][1]]=2;f[1][1][p-1]=1;q=f[1][0][0];
    for (i=1;i<=n;i++){
        for (j=1;j<m;j++)
            for (k=0;k<p;k++){
                if (f[i][j][k]==q) continue;
                x=a[i][j+1];
                if (k%3==2) x^=1;if (k/er[m]==2&&i!=1) x^=1;
                if (k/er[m]==1){
                    t=k%er[m]*3+2;
                    if (k%3==0) t+=3;
                    else if (k%3==1) t-=3;
                    f[i][j+1][t]=min(f[i][j+1][t],f[i][j][k]+1);
                }else{
                    f[i][j+1][k%er[m]*3+x]=min(f[i][j+1][k%er[m]*3+x],f[i][j][k]);
                    f[i][j+1][k%er[m]*3+1-x]=min(f[i][j+1][k%er[m]*3+1-x],f[i][j][k]+2);
                    if (k/er[m]){
                        t=k%er[m]*3+2;
                        if (k%3==0) t+=3;
                        else if (k%3==1) t-=3;
                        f[i][j+1][t]=min(f[i][j+1][t],f[i][j][k]+1);
                    }
                }
            }
        if (i==n) continue;
        for (k=0;k<p;k++){
            if (f[i][m][k]==q) continue;
            x=a[i+1][1];
            if (k/er[m]==2) x^=1;
            if (k/er[m]==1) f[i+1][1][k%er[m]*3+2]=min(f[i+1][1][k%er[m]*3+2],f[i][j][k]+1);
            else{
                f[i+1][1][k%er[m]*3+x]=min(f[i+1][j][k%er[m]*3+x],f[i][j][k]);
                f[i+1][1][k%er[m]*3+1-x]=min(f[i+1][1][k%er[m]*3+1-x],f[i][j][k]+2);
                if (k/er[m]) f[i+1][1][k%er[m]*3+2]=min(f[i+1][1][k%er[m]*3+2],f[i][j][k]+1);
            }
        }
    }ans=q;
    for (i=0;i<p;i++){
        t=0;x=i;
        while (x){
            if (x%3==1) t++;
            x/=3;
        }
        if (t) continue;
        ans=min(ans,f[n][m][i]);
    }
    printf("%d\n",ans);
    }
}