18 E Flag 2
問題
nxmマスのグリッドがあり、それぞれの色が英小文字一文字で表されている。
このグリッドを、次の条件を満たすように塗り替えたい。
- 隣り合うマスの色は異なる
- 一行に使われる色は2色以下である(列に関しては関係ない)
この時、マスを書き換える数を最小にして、塗り替えたい。
そのような数および、出来上がるグリッドを出力せよ。
制約条件
n,m≦500
方針
条件から、出来上がるグリッドは、各行最初の2マスを決めれば一意に定まることがわかる。
したがって、
dp[行][1文字目][2文字目]を、そのような状態にするような最小の書き換え回数とするDPにより解ける。
出来上がるグリッドの出力も必要なので、経路復元も行う。
これは、ナイーブに実装するとO(n*m*26^4)となりTLEなので、
各行を、最初の2マスをa,bにするようなコストをあらかじめ計算しておくことにより
計算量をO(n*26^4)に落とす必要がある。
ソースコード
int h,w; char m[500][501], am[500][501]; int dp[501][30][30], prev[501][30][30]; int cost[500][30][30]; void run(){ cin>>h>>w; rep(i,h)cin>>m[i]; rep(i,h){ rep(a,26)rep(b,26){ int c=0; for(int j=0;j<w;j+=2)c+=m[i][j]-'a'!=a; for(int j=1;j<w;j+=2)c+=m[i][j]-'a'!=b; cost[i][a][b]=c; } } rep(i,h+1)rep(j,30)rep(k,30)dp[i][j][k]=1e9; dp[0][26][26]=0; rep(i,h)rep(j,27)rep(k,27)if(dp[i][j][k]!=1e9){ rep(a,26)rep(b,26)if(a!=b&&a!=j&&b!=k){ int nxt=dp[i][j][k]+cost[i][a][b]; if(dp[i+1][a][b]>nxt)dp[i+1][a][b]=nxt, prev[i+1][a][b]=j*30+k; } } int ans=1e9, ai,aj; rep(i,26)rep(j,26)if(ans>dp[h][i][j])ans=dp[h][i][j], ai=i, aj=j; for(int i=h-1;i>=0;i--){ for(int j=0;j<w;j+=2)am[i][j]=ai+'a'; for(int j=1;j<w;j+=2)am[i][j]=aj+'a'; int bi=prev[i+1][ai][aj]/30, bj=prev[i+1][ai][aj]%30; ai=bi; aj=bj; } cout<<ans<<endl; rep(i,h)cout<<am[i]<<endl; }