卧薪尝胆,厚积薄发。
SDOI2009 bill的挑战
Date: Wed May 23 16:30:00 CST 2018 In Category: NoCategory

Description:

给出 $N$ 个串,每个串由小写字母或‘ ? ’组成,求与其中恰好 $K$ 个串匹配的字符串个数
$ 1 \le N \le 15 $ $ len \le 50 $

Solution:

$ f[i][j] $ 表示前 $i$ 位恰好与集合 $j$ 中的串匹配的字符串个数,注意是恰好,转移时枚举当前选哪个字符,注意需要保证"恰好"条件,设第 $i$ 位选字符 $j$ 能转移的集合为 $tag[i][j]$ ,则转移方程为:
$ f[i][k $ $\&$ $ tag[i][j]] $ = $\sum_{j='a'}^{'z'} f[i - 1][k]$
考虑它是怎样保证恰好条件的:若当前状态能保证恰好,则集合 $k$ 中可以从 $j$ 转移的子集( $k\&tag[i][j]$ )加上一个字符 $j$ 一定也不存在一个统计上的字符串可以满足其他状态

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MOD 1000003
#define MAXN 16
#define MAXL 52
int cnt[1 << MAXN];
int testcases;
int n,k;
int l;
int f[MAXL][1 << MAXN];
char c[MAXN][MAXL];
void work()
{
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i)
{
scanf("%s",c[i]);
}
l = strlen(c[1]);
for(int i = 1;i <= n;++i)
{
for(int j = l;j >= 1;--j)
{
c[i][j] = c[i][j - 1];
}
}
memset(f,0,sizeof(f));
int tot = (1 << n) - 1;
f[0][tot] = 1;
for(int i = 1;i <= l;++i)
{
for(int j = 1;j <= 26;++j)
{
int tag = 0;
for(int t = 1;t <= n;++t)
{
if(j + 'a' - 1 == c[t][i] || c[t][i] == '?')
{
tag |= (1 << (t - 1));
}
}
for(int k = 0;k <= tot;++k)
{
f[i][k & tag] = (f[i][k & tag] + f[i - 1][k]) % MOD;
}
}
}
int ans = 0;
for(int i = 0;i <= tot;++i)
{
if(cnt[i] == k)
{
ans = (ans + f[l][i]) % MOD;
}
}
cout << ans << endl;
return;
}
int main()
{
cnt[0] = 0;
for(int i = 1;i < (1 << MAXN);++i)
{
if(i & 1)cnt[i] = cnt[i >> 1] + 1;
else cnt[i] = cnt[i >> 1];
}
scanf("%d",&testcases);
for(int i = 1;i <= testcases;++i)work();
return 0;
}
In tag: DP-状压DP
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡