CODE FESTIVAL 2016 qual C Friction(800)
問題
考察
操作によって生じるコストを、生じる場所によって別々に考えてみることにします。そうすると、操作によってかかるコストの総和の下界がであることが分かります1。これが達成可能かどうかを考えてみます。
列に対する操作を整数で表すことにします。まず、各に対し、列と列の間で生じるコストの総和が最小となるような操作列を取ってきます。これらをもとに、全体に対する操作列を作ることを考えます。列と列の間でかかるコストは、列と列をどのような順番で操作したかだけによります。よって、全体に対する操作列の中で、とが取ってきた操作列と同じ順番で並んでいればよいです。このような操作列は、まずとを並べてから、を順番に適切な位置に(取ってきた操作列と同じ順番で現れるように)挿入すれば作ることができます。
これで下界が達成できることが分かりました。この下界を求めるためにはの場合を解けばよく、DPを頑張るとできます。
#include<bits/stdc++.h> using namespace std; const int INF=1e9; int main() { int H,W; cin>>H>>W; vector<string> s(H); for(int i=0;i<H;i++) cin>>s[i]; int ans=0; for(int j=0;j<W-1;j++){ // cost[x][y]:=j列目がx個、j+1列目がy個の状態のときに操作にかかるコスト vector<vector<int>> cost(H+1,vector<int>(H+1)); for(int x=0;x<=H;x++) for(int y=0;y<=H;y++){ if(x==0 or y==0) cost[x][y]=0; else cost[x][y]=cost[x-1][y-1]+(s[x-1][j]==s[y-1][j+1]); } // dp[x][y]:=j列目がx個、j+1列目がy個の状態にするのにかかるコストの総和のmin vector<vector<int>> dp(H+1,vector<int>(H+1,INF)); dp[H][H]=0; for(int x=H;x>=0;x--) for(int y=H;y>=0;y--){ if(x+1<=H) dp[x][y]=min(dp[x][y],dp[x+1][y]+cost[x+1][y]); if(y+1<=H) dp[x][y]=min(dp[x][y],dp[x][y+1]+cost[x][y+1]); } ans+=dp[0][0]; } cout<<ans<<endl; return 0; }
まとめ
- コスト(など)が複数の場所で生じるとき、各場所ごとに考える
- 下界が達成可能か
感想
コストを生じる場所ごとに考えるという思考が重要だったと思います。うまく言語化できていませんが、これはbit演算が出てくる問題で、各桁ごとに見るという思考と似ていると思いました。
-
全体に対して適当に操作したときにi列目とi+1列目の間で生じるコストは、i列目とi+1列目で生じるコストが最小になるように操作したときのコスト以上になっているはずだからです。↩