[SDOI2009] Bill的挑战 (状压DP)
[SDOI2009] Bill的挑战
题目描述
Sheng_bill 不仅有惊人的心算能力,还可以轻松地完成各种统计。在昨天的比赛中,你凭借优秀的程序与他打成了平局,这导致 Sheng_bill 极度的不满。于是他再次挑战你。这次你可不能输。
这次,比赛规则是这样的:
给出 \(N\) 个长度相同的字符串(由小写英文字母和 ?
组成),\(S_1,S_2,\dots,S_N\),求与这 \(N\) 个串中的刚好 \(K\) 个串匹配的字符串 \(T\) 的个数,答案对 \(1000003\) 取模。
若字符串 \(S_x(1\le x\le N)\) 和 \(T\) 匹配,满足以下条件:
- \(|S_x|=|T|\)。
- 对于任意的 \(1\le i\le|S_x|\),满足 \(S_x[i]= \texttt{?}\) 或者 \(S_x[i]=T[i]\)。
其中 \(T\) 只包含小写英文字母。
输入格式
本题包含多组数据。
第一行一个整数 \(T\),表示数据组数。
对于每组数据,第一行两个整数,\(N\) 和 \(K\)。
接下来 \(N\) 行,每行一个字符串 \(S_i\)。
输出格式
每组数据输出一行一个整数,表示答案。
样例 #1
样例输入 #1
5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
样例输出 #1
914852
0
0
871234
67018
提示
数据规模与约定
- 对于 \(30\%\) 的数据,\(N\le5\),\(|S_i|\le20\);
- 对于 \(70\%\) 的数据,\(N\le13\),\(|S_i|\le30\);
- 对于 \(100\%\) 的数据,\(1\le T\le 5\),\(1\le N \le15\),\(1\le|S_i|\le50\)。
分析
题目给的\(|S|\)其实就是\(S.lenth\)
不会有人和我一样一眼没看出来吧
看眼数据不难想到应该枚举位数,那么维护数组\(g[i][j]\)表示第\(i\)个位数放\(j\)的情况下该列的情况
定义\(dp[i][j]\)为\(T\)串已经匹配了\(i\)位,且与\(n\)个字符串是否匹配的集合为\(j\),状态边界为\((1<<n)\)
所以\(dp[0][(1<<n)-1]=1\)
首先枚举位数,然后枚举状态
如果\(dp[i][j]==0\)不需要进行操作
之后枚举字符,在下一状态下添加字符的种类数为本状态加上下一状态的原种类数
得到的状态转移方程即为
最后枚举不同状态,记录该状态与原数组的匹配情况
判断该状态是否包括某一行的位数(即该行匹配)
如果是则\(tot++\),如果\(tot=m\),叠加\(dp[len][当前状态]\),得到\(ans\)
最后别忘了要取mod
code
Elaina's code
#include<bits/stdc++.h>
using namespace std;
const int N=50100;
const double eps=1e-8;
#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define re register
#define Elaina 0
inline int read(){
int x=0,f=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
}
int mod=1e6+3;
int dp[55][1<<15],g[55][30];
char s[16][55];
int T,n,m;
main(){
T=read();
while(T--){
mst(dp,0);
mst(g,0);
n=read(),m=read();
for(int i=0;i<n;i++){
scanf("%s",s[i]);
}
int len=strlen(s[0]);
for(int i=0;i<len;i++){//位数
for(int j=0;j<26;j++){//字符
for(int k=0;k<n;k++){//行数
if(s[k][i]=='?'||s[k][i]==j+'a'){
g[i][j]|=(1<<k);//匹配情况
}
}
}
}
dp[0][(1<<n)-1]=1;
for(int i=0;i<len;i++){//位数
for(int j=0;j<(1<<n);j++){//状态
if(dp[i][j]){
for(int k=0;k<26;k++){//字符
dp[i+1][j&g[i][k]]=(dp[i+1][j&g[i][k]]+dp[i][j])%mod;
}
}
}
}
int ans=0;
for(int i=0;i<(1<<n);i++){//状态
int tot=0;
for(int j=0;j<n;j++){
if(i&(1<<j)){
tot++;
}
}
if(tot==m){
ans=(ans+dp[len][i])%mod;
}
}
printf("%lld\n",ans);
}
return Elaina;
}
/*
5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??
*/