UOJ#748-[UNR #6]机器人表演【dp】
正题
题目链接:https://uoj.ac/problem/748
题目大意
有一个长度为\(n\)的\(01\)序列,然后\(t\)次插入一个\(0\)和一个\(1\),要求\(0\)在\(1\)前面,求最终能得到多少种本质不同的串。
\(1\leq n,t\leq 300\)
解题思路
我们考虑一个\(n+2\times t\)的\(01\)串是否合法,而且我们最好能搞出一种记录信息最少且唯一的方法。
我们记录一个\(x\)表示当前匹配到的位置,当我们加入一个\(0\)或\(1\)时,如果恰好能和下一个匹配,我们就匹配。否则如果是\(0\),我们再记录一个\(y\)表示目前有多少个未匹配的\(0\)。如果是\(1\),如果前面有未匹配的\(0\),我们就用未匹配的\(0\)和这个\(1\)匹配。
如果没有我们就一直让匹配位置\(x\)往前走,直到出现一个未匹配的\(0\),我们可以先预处理出一个\(p_i\)表示匹配位置\(i\)往前跳到出现第一个未匹配\(0\)的位置。
这种匹配方法一定是最优的,因为往前跳一到的位置一定是一个\(0\),而之后我们拿未匹配的\(0\)去匹配这个\(0\)显然不优秀。
然后我们设\(f_{i,j,k}\)表示现在填到第\(i\)个,目前匹配到位置\(j\),前面有\(k\)个未匹配的\(0\)时前面填的方案数转移即可。
时间复杂度:\(O(n^3)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=305,P=998244353;
int n,t,f[N*3][N][N],pre[N];
char s[N];
void Add(int &x,int y)
{x=(x+y>=P)?(x+y-P):(x+y);}
int main()
{
// printf("%d\n",sizeof(f)>>20);
scanf("%d%d",&n,&t);
scanf("%s",s+1);pre[0]=-1;
for(int i=1;i<=n;i++){
int p=0;
for(int j=i;j>=1;j--){
if(s[j]=='1')p++;
else p--;
if(p==-1){pre[i]=j-1;break;}
}
if(p!=-1)pre[i]=-1;
}
f[0][0][0]=1;
for(int i=0;i<n+2*t;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=t;k++){
if(!f[i][j][k])continue;
//zero
if(s[j+1]=='0')Add(f[i+1][j+1][k],f[i][j][k]);
else Add(f[i+1][j][k+1],f[i][j][k]);
//one
if(s[j+1]=='1')Add(f[i+1][j+1][k],f[i][j][k]);
else{
if(k)Add(f[i+1][j][k-1],f[i][j][k]);
else if(pre[j]!=-1)Add(f[i+1][pre[j]][k],f[i][j][k]);
}
}
printf("%d\n",f[n+2*t][n][0]);
return 0;
}