- H行W列のグリッドがある
- 0行0列(0,0) から、 H-1行W-1列(H-1,W-1)に移動することを考える。
- 各マスは0,1で構成されている。
- 移動は同じ数字の間でしか移動できない
でもって
- 各行、各列に対してお金を払って0と1を反転することが出来る
- 最小のお金で(0,0)->(H-1,W-1)への移動をしろ
0 | 0 | 0 |
---|---|---|
0 | 0(☆) | 0 |
1 | 1 | 1 |
1 | 1 | 1 |
int now = S[0][0]; //とりあえず、スタートマスの数字に合わせて移動する
rep(i,h){
rep(j,w){
bool same = (now == S[i][j+1]);//入力で与えられた盤面を見たとき、横の移動先はnowか?
bool same2 = (now == S[i+1][j]);//縦の移動先はnowか?
//ちなみに、移動前がnowと一致していることはDP的に保障されている。そういう風に実装している
rep(x,2){
rep(y,2){
// dp[i][j][x][y]からの遷移
//横の移動
if((same && x) || (!same && !x)){
// 移動先は目的の数字であるが、行に対する操作を施してしまっている。あるいは、移動先が目的の数字でなくて、行に対する操作もしていないというとき。
// 2パターンと言ったが、「要は横の移動先が移動前と数字が異なるとき」
//この2パターンは、列に対する操作でしかnowに一致させることが出来ないので、列に対する操作をする。(移動前に列に対する操作をしているかどうかは関係ないので、chmin(a,b)のbのyは0でも1でもおk
chmin(dp[i][j+1][x][1], dp[i][j][x][y] + C[j+1]);
} else {
// 移動の前後で数字が変わらないので、課金額0円で遷移可能
chmin(dp[i][j+1][x][0], dp[i][j][x][y]);
}
//縦の移動
if((same2 && y) || (!same2 && !y)){
//(縦の移動前後で数字が異なるとき)、移動先の行に対する操作でnowと一致させないといけない
chmin(dp[i+1][j][1][y], dp[i][j][x][y] + R[i+1]);
}else {
chmin(dp[i+1][j][0][y], dp[i][j][x][y] );
}
}
}
}
}
ll ans = INF;
rep(x,2)rep(y,2){
chmin(ans,dp[h-1][w-1][x][y]);
}
//ansは候補の値的なイメージ
rep(i,2010)rep(j,2010)rep(x,2)rep(y,w)dp[i][j][x][y] = INF;
//dp[i][j][x][y]:= (i,j)
dp[0][0][0][1] = C[0];//S[0][0]じゃない方にするので、行に対する操作か、列に対する操作を最初のマスにしとかないといけない
dp[0][0][1][0] = R[0];
now = 1-now;
rep(i,h){
rep(j,w){
bool same = (now == S[i][j+1]);
bool same2 = (now == S[i+1][j]);
rep(x,2){
rep(y,2){
// dp[i][j][x][y]からの繊維
if((same && x) || (!same && !x)){
chmin(dp[i][j+1][x][1], dp[i][j][x][y] + C[j+1]);
} else {
chmin(dp[i][j+1][x][0], dp[i][j][x][y]);
}
if((same2 && y) || (!same2 && !y)){
chmin(dp[i+1][j][1][y], dp[i][j][x][y] + R[i+1]);
}else {
chmin(dp[i+1][j][0][y], dp[i][j][x][y] );
}
}
}
}
}
rep(x,2)rep(y,2){
chmin(ans,dp[h-1][w-1][x][y]);
}
cout << ans << endl;