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;
}
posted @ 2026-06-23 19:47  海因里奇  阅读(5)  评论(0)    收藏  举报