1. 程式人生 > >Happy Matt Friends(利用滾動陣列優化陣列空間的DP)

Happy Matt Friends(利用滾動陣列優化陣列空間的DP)

Happy Matt Friends

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


 

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   |   We have carefully selected several similar problems for you:  6447 6446 6445 6444 6443 

有一個bug:題目雖然告訴你數<10^6,也就是2^17,理論上來說異或和不會到2^18

但是你開到2^18就錯了,還是開到20,其實19也行

 題意:
  matt有N個朋友,現在需要從中選出n個(0<=n<=N),使得每個朋友的ki值,異或結果(^)大於等於M。
思路:
  首先注意題目資料範圍: N<=40   M<=10^6
  先排除暴力列舉的情況,
dp思路:
  與揹包類似:  考慮第i+1個人,選或不選
  f[i][j]表示前i個人,選擇其中k個(k<=i),使得異或結果為j。
  遞推式:
    不選第i個人:  f[i+1][j] += f[i][j];   // 這裡ans不進行累加,因為兩種情況為同一種情況! 
    選擇第i個人:  f[i+1][j^a[i]]  +=  f[i][j];此時,如果當前異或結果(j^a[i])>=M, 則加入最終ans中: ans += f[i][j];
 
·注意: 本題也可以全部計算出 f[i][j],使用雙重迴圈,求sum{f[n][j](j>=m)},即為結果。

 

對  if(m==0) num++;  的理解:(個人理解,如果不對歡迎大家糾錯!!)

  當m為0的時候,一個人都不選,可以為一種情況,加上這種情況。(當m不為0的時候,至少需要選擇1個人。)

初始化 f[0][0] = 1; 的理解:

  前0個人,異或結果為0時,為一種情況,結果為1.(即上面提到的)

  同時這裡因為雙重for迴圈中每次加的都是f[i+1][j],所以不會計算重複!
 

#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#define ll long long
using namespace std;
const int N=1<<20;
int dp[64][N];
int a[45];
int main()
{
    int t;
    scanf("%d",&t);
    int f=0;
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        ll ans=0;
        if(m==0)ans++;
        for(int i=0; i<n; i++)
            for(int j=0; j<N; j++)
            {//不選的時候,  當前選擇狀況沒變, 不需要 num 加上相應情況值。
                dp[i+1][j]+=dp[i][j];
                int t=j^a[i];
                dp[i+1][t]+=dp[i][j];
                if(t>=m)ans+=dp[i][j];
                 //注意這裡是增加的f[i][j] !!! 把每次累加的加入進來即可
            }
        f++;
        printf("Case #%d: %lld\n",f,ans);
    }

    return 0;
}