【2018百度之星资格赛】 A 问卷调查 - 位运算&动规

题目地址:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=820&pid=1001

参考博客:在此感谢http://www.cnblogs.com/LQLlulu/p/9419232.html博主,多谢指教,受益良多

Summarize:

  1. 使用动态规划的方法,状态转移方程:f[i][p]=f[i-1][p]+i-和第i个答案集合相同的数目;

  2. 转移方程中i表示第i份答卷,p表示被选中问题集合,因为每个问题只有A,B两种答案,故可使用二进制表示,1表示选‘A’,2表示选‘B’;

  3. p则表示问题集合,从01至(1<<m)-1循环;

  4. “+i”表示假设前i份问卷都与i不同(包括i本身);

  5. PS: 注意数组范围,虽然题目只给了n在1000以内,但通过刚才的分析我们发现,vis数组因为开到了(1<<m)-1,所以至少要开到1<<m,即1024!!

附代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define LL long long
 8 const int N = 1025;
 9 int T, n, m, k, f[N][1<<11], vis[N];
10 char text[N][15];
11 
12 int main()
13 {
14     scanf("%d", &T);
15     for(int t=1; t<=T; t++)
16     {
17         scanf("%d%d%d", &n, &m, &k);
18         
19         for(int i=1; i<=n; i++)
20             scanf("%s", text[i]);
21         
22         int all = (1<<m)-1;
23         for(int p=0; p<=all; p++) {
24             memset(vis, 0, sizeof(vis));
25             for(int i=1; i<=n; i++) {
26                 int w=0;
27                 for(int j=0; j<m; j++) {
28                     if( p&(1<<j) && text[i][j]=='A')
29                         w|=(1<<j);
30                 }
31                 vis[w]++;
32                 f[i][p] = f[i-1][p]+i-vis[w];
33               
34             }
35         }
36         
37         int ans=0;
38         for(int i=0; i<(1<<m); i++)
39             if(f[n][i]>=k)
40                 ans++;
41         printf("Case #%d: %d\n", t, ans);
42     }
43     
44     return 0;
45 }

猜你喜欢

转载自www.cnblogs.com/liubilan/p/9424727.html