题解:freee Programming Contest 2022(AtCoder Beginner Contest 264)之F - Monochromatic Path (atcoder.jp)
·难度
算法难度:普及
思维难度:提高
调码难度:提高
综合评价:困难
·算法
动态规划+问题拆分
·思路
状态:f[i][j][x][y]表示考虑到第i行第j列、第i行被翻转了x次、第j列被翻转了y次。根据!的性质可以得出x、y是0、1之中的数。
初值:f[1][1][0][0]=0;f[1][1][1][0]=r[i];f[1][1][0][1]=c[j]; f[1][1][1][1]=r[i]+c[j];
转移:
if(i!=1){
if(a[i][j]==a[i-1][j]){
f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][0][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][1][0]+r[i]);
f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][0][1]);
f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][1][1]+r[i]);
}else{
f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][1][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][0][0]+r[i]);
f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][1][1]);
f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][0][1]+r[i]);
}
}
if(j!=1){
if(a[i][j]==a[i][j-1]){
f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][0]);
f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][1]+c[j]);
f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][1]+c[j]);
}else{
f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][1]);
f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][1]);
f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][0]+c[j]);
f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][0]+c[j]);
}
}
原理自己理解……
好吧我说一说:
其实对于四种i、j的子状态的原理类似,就是如果上面有元素就用上面的元素转移它,左侧有元素就有左侧的元素转移它,如果都有就分别转移。转移分为两大类:被转移元素颜色与转移元素颜色相同时,需要让两元素所在行或列翻转次数除以2的余数相同,若颜色不同则让余数差1。需要注意的有两点:每次转移时需要将本次翻转的代价加上;被转移元素和转移元素的翻转操作可能重合(比方说在同一行的两个元素共用一个行翻转操作),且不能有矛盾(比方说被转移元素状态为i、j、1、1,而我们却用i、j-1、0、0【在被转移元素左侧】转移它肯定不行,因为我们无论是对于被转移元素还是转移元素,实际上都是做了换行操作的,因此不应该是{0,0},而应该是{1,0})。
好难……()这题是给人出的吗
·代码
#include<bits/stdc++.h>
#define M 2200
using namespace std;
long long f[M][M][2][2]={};
int a[M][M]={},c[M]={},r[M]={},h=0,w=0;
int main(){
scanf("%d%d",&h,&w);
for(int i=1;i<=h;i++){
scanf("%d",&r[i]);
}
for(int i=1;i<=w;i++){
scanf("%d",&c[i]);
}
for(int i=1;i<=h;i++){
string str="";
cin>>str;
for(int j=1;j<=w;j++){
a[i][j]=str[j-1]-'0';
}
//注意:这里由于中间没有空格,需要用字符串输入,而字符串是从0开始的
}
for(int i=1;i<=h;i++){
for(int j=1;j<=w;j++){
if(i==1&&j==1){
f[i][j][0][0]=0;
f[i][j][1][0]=r[i];
f[i][j][0][1]=c[j];
f[i][j][1][1]=r[i]+c[j];
//对于{1,1,x,y}进行初始化
}else{
f[i][j][0][0]=1LL<<60;
f[i][j][1][0]=1LL<<60;
f[i][j][0][1]=1LL<<60;
f[i][j][1][1]=1LL<<60;
//每个状态初值设成无穷大
if(i!=1){
if(a[i][j]==a[i-1][j]){
f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][0][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][1][0]+r[i]);
f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][0][1]);
f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][1][1]+r[i]);
}else{
f[i][j][0][0]=min(f[i][j][0][0],f[i-1][j][1][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i-1][j][0][0]+r[i]);
f[i][j][0][1]=min(f[i][j][0][1],f[i-1][j][1][1]);
f[i][j][1][1]=min(f[i][j][1][1],f[i-1][j][0][1]+r[i]);
}
}
if(j!=1){
if(a[i][j]==a[i][j-1]){
f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][0]);
f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][0]);
f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][1]+c[j]);
f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][1]+c[j]);
}else{
f[i][j][0][0]=min(f[i][j][0][0],f[i][j-1][0][1]);
f[i][j][1][0]=min(f[i][j][1][0],f[i][j-1][1][1]);
f[i][j][0][1]=min(f[i][j][0][1],f[i][j-1][0][0]+c[j]);
f[i][j][1][1]=min(f[i][j][1][1],f[i][j-1][1][0]+c[j]);
}
}
//楠楠的转移
}
}
}
printf("%lld\n",min(min(f[h][w][0][0],f[h][w][0][1]),min(f[h][w][1][0],f[h][w][1][1])));
//答案是走到右下角每种情况的最小值
return 0;
}
·注意
一定要开long long,否则参见WA提交记录。
转移需注意(别整错了)。