P2167 [SDOI2009]Bill的挑战
题目描述
输入输出格式
输入格式:本题包含多组数据。 第一行:一个整数T,表示数据的个数。 对于每组数据: 第一行:两个整数,N和K(含义如题目表述)。 接下来N行:每行一个字符串。
输出格式:如题
输入输出样例
输入样例#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%的数据,T ≤ 5,M ≤ 5,字符串长度≤ 20;
对于70%的数据,T ≤ 5,M ≤ 13,字符串长度≤ 30;
对于100%的数据,T ≤ 5,M ≤ 15,字符串长度≤ 50。
题解:
不知道为什么被我水过了,设dp[i][S]表示当前正在dp第i位,S表示有哪些串已经不合法了(合法为1,不合法为0),转移的话枚举一个字母,然后填上去,判断一下现在有那些串不合法就可以了。
状态有50*2^15个,转移是15*26的,这样子都可以水过去,本来想先写一个,优化一下的……
代码:
// luogu-judger-enable-o2 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #define RG register #define mod 1000003 #define ll long long using namespace std; char a[100][100]; ll dp[52][(1<<17)+1];int n,K,len; int getnum(int S){ int num=0; while(S!=0){ if(S%2==1) num++; S/=2; } return num; } void work(){ cin>>n>>K; for(int i=1;i<=n;i++) scanf("%s",a[i]+1); len=strlen(a[1]+1); memset(dp,0,sizeof(dp)); dp[1][(1<<n)-1]=1; for(RG int i=1;i<=len;i++){ for(RG int S=(1<<n)-1;S>0;S--){ if(dp[i][S]!=0){ if(getnum(S)<K) continue; for(int now=0;now<=25;now++){ int toS=S; for(int k=1;k<=n;k++){ if(a[k][i]!='?'&&a[k][i]!='a'+now){ if(S&(1<<(k-1))) toS-=(1<<(k-1)); } } dp[i+1][toS]+=dp[i][S],dp[i+1][toS]%=mod; } } } } ll ans=0; for(int S=(1<<n)-1;S>0;S--){ if(getnum(S)==K) ans+=dp[len+1][S],ans%=mod; } printf("%lld\n",ans); } int main() { int t;cin>>t; while(t--) work(); return 0; }