BZOJ1879:[SDOI2009]Bill的挑战(状压DP)

Description

Input

本题包含多组数据。 
第一行:一个整数T,表示数据的个数。 
对于每组数据: 
第一行:两个整数,N和K(含义如题目表述)。 
接下来N行:每行一个字符串。
T ≤ 5,M ≤ 15,字符串长度≤ 50。

Output

如题

Sample Input

5
3 3
???r???
???????
???????
3 4
???????
?????a?
???????
3 3
???????
?a??j??
????aa?
3 2
a??????
???????
???????
3 2
???????
???a???
????a??

Sample Output

914852
0
0
871234
67018

Solution

第一眼数据范围:撞鸭状压DP没跑了
而且连压什么都告诉你了,毕竟只有N能压
状态设计很简单:f[i][S]该选第i列了,当前选中的行是集合S
然后我后面就G了……搞了半天又是容斥又是各种判断乱搞只有20……
其实预处理一下就非常好做了。
预处理出g[第i列][j字母]=集合x
表示j字母可以和集合x的第i位匹配(我第一次看的时候有点绕)
然后就枚举当为集合x的时候第i位填j字母然后进行转移就好了
果然我就是R1出题人说的"学数据结构学傻了"
虽然我数据结构仍然辣鸡

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define MAXN (40001)
 5 using namespace std;
 6 int f[52][MAXN],g[52][31];
 7 int N,K,T,len,sum;
 8 char s[101][101];
 9 
10 int main()
11 {
12     scanf("%d",&T);
13     while (T--)
14     {
15         memset(f,0,sizeof(f));
16         memset(g,0,sizeof(g));
17         scanf("%d%d",&N,&K);
18         for (int i=1; i<=N; ++i)
19             scanf("%s",s[i]+1);
20         len=strlen(s[1]+1);
21         
22         for (int i=1; i<=len; ++i)
23             for (int j=0; j<26; ++j)
24                 for (int k=1; k<=N; ++k)
25                     if (s[k][i]=='?' || s[k][i]==j+'a')
26                         g[i][j]|=(1<<k-1);
27         
28         sum=(1<<N)-1;
29         f[0][sum]=1;            
30         for (int i=1; i<=len; ++i)
31             for (int j=0; j<=sum; ++j)
32                 if (f[i-1][j])//不加这个判断会TLE,可能是%太多了? 
33                     for (int k=0; k<26; ++k)
34                         (f[i][j&g[i][k]]+=f[i-1][j])%=1000003;
35 
36         int ans=0;
37         for (int i=0; i<=sum; ++i)
38         {
39             int x=i,cnt=0;
40             while (x)
41             {
42                 if (x&1) cnt++;
43                 x>>=1;
44             }
45             if (cnt==K) (ans+=f[len][i])%=1000003;
46         }
47         printf("%d\n",ans);
48     }
49 }
posted @ 2018-04-18 20:02  Refun  阅读(212)  评论(0编辑  收藏  举报