2014年北京 happy matt friends(dp + 滚动数组优化)

Happy Matt Friends

Time Limit: 6000/6000 MS (Java/Others)    Memory Limit: 510000/510000 K (Java/Others)
Total Submission(s): 6164    Accepted Submission(s): 2330


Problem Description
Matt has N friends. They are playing a game together.

Each of Matt’s friends has a magic number. In the game, Matt selects some (could be zero) of his friends. If the xor (exclusive-or) sum of the selected friends’magic numbers is no less than M , Matt wins.

Matt wants to know the number of ways to win.
 
Input
The first line contains only one integer T , which indicates the number of test cases.

For each test case, the first line contains two integers N, M (1 ≤ N ≤ 40, 0 ≤ M ≤ 106).

In the second line, there are N integers ki (0 ≤ ki ≤ 106), indicating the i-th friend’s magic number.
 
Output
For each test case, output a single line “Case #x: y”, where x is the case number (starting from 1) and y indicates the number of ways where Matt can win.
 
Sample Input
2 3 2 1 2 3 3 3 1 2 3
 
Sample Output
Case #1: 4 Case #2: 2
Hint
In the first sample, Matt can win by selecting: friend with number 1 and friend with number 2. The xor sum is 3. friend with number 1 and friend with number 3. The xor sum is 2. friend with number 2. The xor sum is 2. friend with number 3. The xor sum is 3. Hence, the answer is 4.
 
Source
 
Recommend
liuyiding
 
这题比赛时候居然没有做出来呜呜呜,我对dp的学习路线让人怀疑。。。。
 
接下来分析一下这个题
 
本题大意:给定n个数,让你在n个数字中选择任意多个数字,使得他们的异或和 >= m。
你不知道某些数字选还是不选,多选或者少选都可能多一种方案,看数据范围数字总共不到40个,
最大值在1e6以内,所以这些值异或之后的最大值也不超过1 << 20,所以应该就要想到一个数字
选还是不选应该是最重要的,他选了之后对于已经选过的值的影响的记录也是必要的,所以肯定要
知道某个值选不选?然后还要把异或之后的值记录下来,记录的话肯定是需要一维记录了,我们想到可以把这些数可以异或得到的所有结果记录下来,然后查询大于等于m的个数有多少个即可。所以索性我们就假设dp[ i ][ j ]为前i个数,选择一些异或起来异或和为 j 的方案数,这样我们很容易就可以得到递推方程dp[ i ][ j ] = dp[i - 1][j ^ a[ i ]] + dp[i - 1][ j ],那就是一个数选或者不选,有两种情况,方法数相加。
 
哦,一看就知道要开4 * 1e7的long long的数组,写出方程后应该可以想到数组是可以滚动的,因为
对于每前i个的计算只需要前i - 1 个数的状态,所以我们索性就开一个dp[ 2 ][ maxn ]的数组,用 & 来滚动数组就行了。哦,具体见代码。
 
 1 /*
 2     本题思路:用dp[i][j]表示前i个数可以凑出数字j的方法数目。
 3     dp[i][j] = dp[i - 1][j ^ a[i]] + dp[i - 1][j];
 4 */
 5 #include <cstdio>
 6 #include <cstring>
 7 #include <algorithm>
 8 using namespace std;
 9 
10 const int maxn = 40 + 5, maxm = 1 << 20;
11 int n, m;
12 
13 typedef long long ll;
14 
15 int a[maxn];
16 
17 ll dp[2][maxm];
18 
19 int main() {
20     int t, _case = 0;
21     scanf("%d", &t);
22     while(t --) {
23         memset(dp, 0, sizeof dp);
24         scanf("%d %d", &n, &m);
25         for(int i = 1; i <= n; i ++) {
26             scanf("%d", &a[i]);
27         }
28         /*
29         dp[0][0] = 1;
30         for(int i = 1; i <= n; i ++) {
31             for(int j = 0; j < maxm; j ++) {
32                 dp[i][j] = dp[i - 1][j ^ a[i]] + dp[i - 1][j];
33             }
34         }
35         */
36        dp[0][0] = 1;
37        for(int i = 1; i <= n; i ++) {
38            for(int j = 0; j < maxm; j ++) {
39                dp[i & 1][j] = dp[i - 1 & 1][j ^ a[i]] + dp[i - 1 & 1][j]; 
40            }
41        }
42         ll ans = 0;
43         for(int i = m; i < maxm; i ++) {
44             ans += dp[n & 1][i];
45         }
46         printf("Case #%d: %lld
", ++ _case, ans);
47     }
48     return 0;
49 }
原文地址:https://www.cnblogs.com/bianjunting/p/11599201.html