P11218 【MX-S4-T2】「yyOI R2」youyou 不喜欢夏天 题解
P11218 【MX-S4-T2】「yyOI R2」youyou 不喜欢夏天 题解
挺好的 dp,感觉难点在于思路转化。
场上 dp 少开了一维,得到了 56pts 的高分,赛后 10min 想出正解,我还是太菜了。
upd:2024/10/24 修改了一点小笔误,望管理员重新审核后通过。
思路
思考一下对于 youyou 和 yy 来说的最优策略是啥。
假设 youyou 选择了一个连通块,并且其中存在一列,youyou 上下都选了,yy 显然不会去交换那一列,因为那样对最终的答案没有影响;如果 youyou 选了某一列一个 0,而另一个没选,yy 也显然不会交换那一列,因为那样对最终答案的影响不优;当且仅当 youyou 选择了某一列的一个 1,而另一个是 0 且 youyou 没选,yy 才会去选择交换。
整理完 yy 的最优策略,对于 youyou 的最优策略也就显然了。对于一列全是 1 来说,youyou 显然会全选;对于一列全是 0 来说,youyou 只可能选其中一个 0,用于延长连通块的长度;对于一列有 1 有 0 来说,youyou 要么全选,要么只选其中的那个 1,显然不可能只选那个 0。
经过一些简单的分析,最终 youyou 想要得到最大的结果,只有可能是在如下两种方案中选择最大值:
- 只要这一列有
1我就全选,yy 没法通过交换上下两个格子的方式使答案变小,最终的答案是选择的1的个数减去选择的0的个数。 - 尽量选择更多的
1,如果上下两个数一1一0,尽量只选那个1,最终答案是选择的1的个数减去选择的0的个数再减去2m 。
第一种方案是显然的,我就不细说了,重点讲一下第二种方案。我们发现,yy 仅会交换选 1 没选 0 的那些列,并且是能换就换,最多交换 m 次。对于最终的答案来说,yy 每做一次有效的交换,都会使最终的答案减少
这时候又会有人说了,如果我第二种方案没有选超过 1 一 0 的情况,那答案不是减去 1 一 0 的列数没有超过 0 只选一个的情况,然而第二种方案不仅这种情况会使答案减少,那些单选一个 1 的列也会使答案减少,故这种情况我们直接忽略掉就好了。
怎么维护呢?对于第一种方案,我们直接跑一个板子最大子段和就好了,详细的说就是把 00 的列视为 10 和 01 的列视为 11 的列视为
简单列一下方案二的状态转移方程,设
其中
方案二最终的答案为
由于最大子段和的 dp 的时间复杂度为
代码
不错的 dp 题。
#include <iostream>
#include <algorithm>
#define ll int
using namespace std;
const ll N=2e6+10;
string a,b;
ll n,m,f[N][5],ya[N],dp[N];
void solve(){
cin>>n>>m;
cin>>a>>b;
a=' '+a;b=' '+b;
for(ll i=1;i<=n;i++){
if(a[i]=='0'&&b[i]=='0') ya[i]=-1;
if(a[i]=='0'&&b[i]=='1') ya[i]=0;
if(a[i]=='1'&&b[i]=='0') ya[i]=0;
if(a[i]=='1'&&b[i]=='1') ya[i]=2;
}
ll ans=0;
for(ll i=1;i<=n;i++){
if(dp[i-1]+ya[i]<ya[i]) dp[i]=ya[i];
else dp[i]=dp[i-1]+ya[i];
ans=max(ans,dp[i]);
}
for(ll i=1;i<=n;i++){
ll A=(a[i]=='1')-(a[i]=='0'),B=(b[i]=='1')-(b[i]=='0');
f[i][0]=max(A,max(A+f[i-1][0],A+f[i-1][2]));
f[i][1]=max(B,max(B+f[i-1][1],B+f[i-1][2]));
f[i][2]=max(max(A+B,A+B+f[i-1][0]),max(A+B+f[i-1][1],A+B+f[i-1][2]));
ans=max(ans,f[i][0]-2*m);
ans=max(ans,f[i][1]-2*m);
ans=max(ans,f[i][2]-2*m);
}
cout<<ans<<"\n";
}
ll c,T;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>c>>T;
while(T--) solve();
return 0;
}