本题给定了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; }