Day7T1
注意到一个性质:
只需要正着操作一遍再反着操作一遍就一定可以。
证明:
使用数学归纳,假设长度小于n的序列均满足该性质,则分类讨论\(s\),\(t\)的第一二个字符:
1.
\(s=R...\)
\(t=R...\)
容易发现只需操作后\(n-1\)个字符,满足。
2.
\(s=RG...\)
\(t=BB...\)
容易发现只需操作第一二个字符\(1\)次,再操作后\(n-2\)个字符,满足。
3.
\(s=RG...\)
\(t=BR...\)
容易发现只需操作\(s\)的第一二个字符\(1\)次,再操作后\(n-1\)个字符,满足。
4.
\(s=RG...\)
\(t=GR...\)
容易发现只需操作第一二个字符\(2\)次,再操作后\(n-2\)个字符,满足。
所以只需要正着反着同时跑一遍dp就行了。
就是这么简单,我也不知道为什么赛时没想到。是因为没见过正反同时跑的dp题?
代码如下:
(这里要把转移代价w预处理出来,要不然跑不过)
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
int dp[N][3][3],w[3][3][3][3];
char s[N],t[N];
int to_num(char c){
if(c=='R') return 0;
if(c=='G') return 1;
if(c=='B') return 2;
}
int ch(pair<int,int> a,pair<int,int> b){
if(a==b) return 0;
if((a.first+a.second)%3!=(b.first+b.second)%3) return 1e8;
if(a.first!=a.second&&b.first!=b.second) return 1e8;
return 1;
}
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
for(int i=0;i<3;i++) for(int j=0;j<3;j++) for(int l=0;l<3;l++) for(int k=0;k<3;k++) w[i][j][l][k] = ch({i,j},{l,k});
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
for(int i=1;i<=n;i++) cin>>t[i];
for(int i=1;i<=n;i++) for(int j=0;j<3;j++) for(int l=0;l<3;l++) dp[i][j][l] = n+1;
dp[1][to_num(s[1])][to_num(t[1])] = 0;
for(int i=2;i<=n;i++){
int a = to_num(s[i]),b = to_num(t[i]);
for(int j=0;j<3;j++){
for(int l=0;l<3;l++){
for(int k=0;k<3;k++){
for(int r=0;r<3;r++){
for(int p=0;p<3;p++){
dp[i][j][l] = min(dp[i][j][l],dp[i-1][k][r]+w[k][a][p][j]+w[r][b][p][l]);
}
}
}
}
}
}
int ans = n+1;
for(int i=0;i<3;i++) for(int j=0;j<3;j++) ans = min(ans,dp[n][i][j]);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号