CF-EDU-175-C. Limited Repainting
C.Limited Repainting
题意简述
给一个初始为长为 \(n\) 的全 \(R\) 字符串 \(S\) ,在给定一个等长目标字符串 \(T\)和一个惩罚数组 \(a\) ,定义惩罚值是 \(S_i \neq T_i\) 下对应的最大 \(a_i\) 现要求最小化惩罚值。
解题思路
最小化最大值,显然存在单调性,不难想到二分枚举答案,难点在 \(check\)函数 的考虑,想要尽可能划分少的段数,这里给出两种考虑的方式,先使用动态规划考虑最优划分方案,定义 \(dp_{ij}\)为仅考虑前i个位置当前位置是\(j(0,1)\)的状态,其中0表示红色,1表示蓝色下选择的最小划分段数,则有转移方程 \(dp_{i,1}=min(dp_{i-1,0}+1,dp_{i-1,1})\),\(dp_{i,0}=min(dp_{i-1,0},dp_{i-1,1})\),当且仅当当前颜色是 \(j\) 或可修改为 \(j\) 时可以发生转移。
从这个线性dp开始思考,会发现最后的贡献只在是\(dp_{i,1}=dp_{i-1,0}+1\) 这里产生,即前一位是红色当前必须是蓝色时对结果产生+1的贡献,这样又可以得到本题的贪心做法:初始化字符变量为 \(R\),当当前惩罚值小于二分枚举的惩罚值时默认覆盖字符串为字符变量,当惩罚值大于二分枚举到的成罚值时修改字符变量的值为当前枚举的 \(S_i\),判断当前\(S\) 和 \(T\)的匹配性,当不匹配且当前目标串 \(T_i=B\)时划分段数增加,最后判断划分段数是否小于k即可
AC code(dp)
void solveDp(){
int n,k;cin>>n>>k;
string s;cin>>s;
s="&"+s;
vector<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
auto check=[&](ll x) ->bool {
vector<vector<int> >dp(n+1,vector<int>(2));
dp[0][1]=n+1;
//考虑前i格,第i格是红,蓝的最小操作数
for(int i=1;i<=n;i++){
dp[i][0]=dp[i][1]=n+1;
if(s[i]=='R'||a[i]<=x) dp[i][0]=min(dp[i-1][1],dp[i-1][0]);
if(s[i]=='B'||a[i]<=x) dp[i][1]=min(dp[i-1][1],dp[i-1][0]+1);
}
return min(dp[n][0],dp[n][1])<=k;
};
ll l=0,r=*max_element(a.begin()+1,a.end());
while(l<=r){
ll mid=l+(r-l)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
cout<<l<<endl;
}
AC code(Greedy)
void solveGreedy(){
int n,k;cin>>n>>k;
string s;cin>>s;
s="&"+s;
vector<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
auto check=[&](ll x) ->bool {
int cnt=0;
char st='R';
for(int i=1;i<=n;i++){
if(a[i]>x){
if(st!='B'&&s[i]=='B') cnt++;
st=s[i];
}
}
return cnt<=k;
};
ll l=0,r=*max_element(a.begin()+1,a.end());
while(l<=r){
ll mid=l+(r-l)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
cout<<l<<endl;
}

浙公网安备 33010602011771号