【2018百度之星资格赛】 A 问卷调查 - 位运算&动规
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6344
参考博客:在此感谢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 }