Codeforces Round #821 (Div. 2) D2

D2. Zero-One (Hard Version)

题意:给定两个长度为 n n n的01串 a , b a,b a,b,每次可以选择一个数对 ( l , r ) (l,r) (l,r),令 a l , a r a_{l},a_{r} al,ar都异或1。如果 l + 1 = = r l+1==r l+1==r,花费 x x x,否则,花费 y y y。求让 a a a变为 b b b的最小花费, ( n < = 5000 ) (n<=5000) (n<=5000)
参考
思路:
·对于特殊情况以及 y ≤ x y \le x yx的情况按D1的解法来。
·对于 x < y x<y x<y的情况考虑采取 O ( N 2 ) O(N^2) O(N2) 的 dp。
状态定义: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示区间 [ i , j ] [i,j] [i,j]的最小花费。
状态转移:区间 [ i , j ] [i,j] [i,j]的最优解只会由 d p [ i + 1 ] [ j − 1 ] dp[i+1][j-1] dp[i+1][j1] d p [ i + 2 ] [ j ] dp[i+2][j] dp[i+2][j] d p [ i ] [ j − 2 ] dp[i][j-2] dp[i][j2]转移而来。
原因:
当x<y时,我们应尽可能的取更多的x,即保留更多的相邻位置。
栗子:
考虑 a a a b b b有六个位置不一样 _ _ _ _ _ _
对于 d p [ 0 ] [ 5 ] dp[0][5] dp[0][5],只能通过 d p [ 1 ] [ 4 ] dp[1][4] dp[1][4] d p [ 0 ] [ 3 ] dp[0][3] dp[0][3] d p [ 2 ] [ 5 ] dp[2][5] dp[2][5]转移过来,这样才能保证保住了更多的相邻位置。
这题的做法有点类似区间dp,但是不像常规的区间dp需要枚举区间断点,因为最优状态只是从有限个小区间转移过来,故复杂度是 O ( N 2 ) O(N^2) O(N2)
参考代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define pb push_back
int T,n,x,y;
string a,b;

int cal(int l,int r){
	if(l+1==r) return min(2*y,x);
	else return min(y,(r-l)*x);
}

int dp[5003][5003];

signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>x>>y>>a>>b;
		//预处理出不相等的位置 
		vector<int> v;
		for(int i=0;i<n;++i){
			if(a[i]!=b[i]) v.pb(i); 
		}
		//特判一些情况 
		if(v.size()==0) cout<<0<<'\n';
		else if(v.size()%2) cout<<-1<<'\n';
		else if(v.size()==2){
			cout<<cal(v[0],v[1])<<'\n';
		}
		else if(y<=x) cout<<v.size()/2*y<<'\n';
		else{
			//在x<y时,这种dp才有效 
			//直接用memset初始化会寄 
			for(int i=0;i<v.size();++i) 
				for(int j=0;j<v.size();++j)
					dp[i][j]=0;
			//防止后面-2导致越界,所以先预处理出长度为2的情况 
			for(int i=0;i+2-1<v.size();++i){
				int j=i+2-1;
				dp[i][j]=cal(v[i],v[j]);
			}
			for(int len=3;len<=v.size();len++){
				for(int i=0;i+len-1<v.size();++i){
					int j=i+len-1;
					dp[i][j]=dp[i+1][j-1]+cal(v[i],v[j]);
					dp[i][j]=min(dp[i][j],dp[i][j-2]+cal(v[j-1],v[j]));
					dp[i][j]=min(dp[i][j],dp[i+2][j]+cal(v[i],v[i+1]));
				}
			}
			cout<<dp[0][v.size()-1]<<'\n';
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值