本题给定了0和1的数量,让你如何排列才能使分数最优。
我们先考虑问题的简单版本,如果给定字符串t全为问号,即对组成的字符串不加限制,就转变经典传纸条问题,即只求一条从(0,0)到(cnt0,cnt1)的路径,只能向右或向下走,使得路径上的点的权值和最大。显而易见的变化中的量就是坐标(x,y),用dp[x][y]来表示传到(x,y)这个坐标的时候,最大(小)的权值和,那么就有 :
其中a[x][y]为到达这点应得的权值,当然对应本题a[x][y]应为判断前缀和后缀的1的数量是否与s串中的相等。
如果字符串t中加上了限制,即在某一特定的位置,确定了走的方向,不能任意走,我们设x,y为0和1的数量(对应坐标),mx[x][y]为对应分数,k为x+y,为当前已走的距离,我们分3总情况讨论:
① t[k]=='0'//只能下走
②t[k]=='1'//只能右走
③t[k]=='?'//可右可下
上述方程中sum[i]为预处理s串中前缀1的数量,用于判断权值,我们可以观察到,在t[k]未确定时,就是确定为0或1两种状态下的最大值,这与上述传纸条问题是相吻合的,所以我们可以将情况3用情况1和2表示出来,关于细节问题在代码中给出
附代码
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int mx[1001][1001],mi[1001][1001],sum[1001];
int main(){
int n,cnt0,cnt1,val0,val1;
cin>>n>>cnt0>>cnt1>>val0>>val1;
string s,t;cin>>s>>t;
for(int i=0;i<s.size();i++)sum[i+1]=sum[i]+s[i]-'0';
t="@"+t;
memset(mi,INF,sizeof(mi));
memset(mx,-INF,sizeof(mx));
mi[0][0] = mx[0][0] = 0;//x,y为0时分数为0
for(int i=0;i<=cnt0;i++){
for(int j=0;j<=cnt1;j++){
int k=i+j;
if(k&&t[k]!='0'&&j>0){
mx[i][j]=max(mx[i][j],mx[i][j-1]+val0*(sum[k]==j)+val1*(sum[n]-sum[k]==cnt1-j));
mi[i][j]=min(mi[i][j],mi[i][j-1]+val0*(sum[k]==j)+val1*(sum[n]-sum[k]==cnt1-j));
//val0*(sum[k]==j)表示前缀1~k得分,val1*(sum[n]-sum[k]==cnt1-j)表示后缀k+1~n得分
}
if(k&&t[k]!='1'&&i>0){
mx[i][j]=max(mx[i][j],mx[i-1][j]+val0*(sum[k]==j)+val1*(sum[n]-sum[k]==cnt1-j));
mi[i][j]=min(mi[i][j],mi[i-1][j]+val0*(sum[k]==j)+val1*(sum[n]-sum[k]==cnt1-j));
}
}
}
if(sum[n]!=cnt1){mi[cnt0][cnt1]-=val1;mx[cnt0][cnt1]-=val1;}
//这里要减掉部分是因为我们可以表示所有前缀得分,但是后缀中1~n这个后缀得分我们并没有表示出来,
//并且后缀中一个空串被我计分了(k=n时),所以我们要判断1~n这个后缀是否得分,如果不得分就要减去空串的得分
cout<<mi[cnt0][cnt1]<<' '<<mx[cnt0][cnt1];
return 0;
}



京公网安备 11010502036488号