牛牛的01限定串
题目描述
链接:https://ac.nowcoder.com/acm/contest/5531/D

思路
本题给定了0和1的数量,让你如何排列才能使分数最优。
我们先考虑问题的简单版本,如果给定字符串t全为问号,即对组成的字符串不加限制,就转变经典传纸条问题,即只求一条从(0,0)到(cnt0,cnt1)的路径,只能向右或向下走,使得路径上的点的权值和最大。显而易见的变化中的量就是坐标(x,y),用dp[x][y]来表示传到(x,y)这个坐标的时候,最大(小)的权值和,那么就有 :

dp[x][y]=max(dp[x−1][y],dp[x][y−1])+a[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’//只能下走

mx[i][j]=max(mx[i][j],mx[i][j−1]+val0∗(sum[k]==j)+val1∗(sum[n]−sum[k]==cnt1−j));

②t[k]==‘1’//只能右走

mx[i][j]=max(mx[i][j],mx[i−1][j]+val0∗(sum[k]==j)+val1∗(sum[n]−sum[k]==cnt1−j));

③t[k]==’?’//可右可下

mx[i][j]=max(mx[i−1][j],mx[i][j−1])+val0∗(sum[k]==j)+val1∗(sum[n]−sum[k]==cnt1−j);

上述方程中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;
}