题目大意:
Matt的n个朋友都有数字,Matt可以选其中几个(可以是零)异或起来,若结果不小于数m,Matt就赢,问Matt赢的方法数。(1 ≤ N ≤ 40, 0 ≤ M ≤ 10 6).
思路:
f[i][j]表示到第i个朋友,异或结果为j的方法数。
转移方程:f[i][j]=f[i-1][j^a[i]]+f[i-1][j]。
我的代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 7 using namespace std; 8 9 const int maxn=(1<<20); 10 11 long long f[41][maxn+1]; 12 int T,n,m,a[41]; 13 14 int main() 15 { 16 scanf("%d",&T); 17 for(int I=1;I<=T;I++) 18 { 19 scanf("%d%d",&n,&m); 20 for(int i=1;i<=n;i++) 21 { 22 scanf("%d",&a[i]); 23 } 24 memset(f,0,sizeof(f)); 25 f[0][0]=1; 26 // cout<<f[n][maxn]; 27 for(int i=1;i<=n;i++) 28 { 29 for(int j=0;j<maxn;j++) 30 { 31 f[i][j]=f[i-1][j^a[i]]+f[i-1][j]; 32 } 33 } 34 //cout<<f[n][maxn]; 35 long long ans=0; 36 for(int i=m;i<maxn;i++) 37 { 38 ans=f[n][i]+ans; 39 // printf("::%I64d\n"); 40 } 41 printf("Case #%d: %I64d\n",I,ans); 42 } 43 return 0; 44 45 }
如果有不幸的同学一直TLE,就试试滚动数组吧(或者用下述方法)。
//队友最初的代码(没改滚动数组版)(加了读入优化,只循环1000000,竟然还TLE):
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 using namespace std; 6 const int maxm=1<<(20); 7 long long f[maxm][41]; 8 int n,m,t; 9 int num[41]; 10 11 int getint() 12 { 13 char ch=getchar(); 14 while(ch<'0'||ch>'9')ch=getchar(); 15 int ret=0; 16 while(ch>='0'&&ch<='9') 17 { 18 ret=ret*10+ch-'0'; 19 ch=getchar(); 20 } 21 return ret; 22 } 23 24 int main() 25 { 26 t=getint(); 27 for(int i=1;i<=t;i++) 28 { 29 memset(f,0,sizeof(f)); 30 long long ans=0; 31 f[0][0]=1; 32 n=getint(); 33 m=getint(); 34 for(int j=1;j<=n;j++) 35 { 36 num[j]=getint(); 37 } 38 for(int j=1;j<=n;j++) 39 { 40 for(int cl=0;cl<=1000000;cl++) 41 { 42 f[cl][j]=f[cl][j-1]+f[cl^num[j]][j-1]; 43 } 44 45 } 46 for(int k=m;k<=1000000;k++) 47 ans+=f[k][n]; 48 printf("Case #%d: %I64d\n",i,ans); 49 } 50 return 0; 51 }
队友代码主要的问题就是f[i][j]中用j表示朋友,用i表示异或和,要是换过来就a了。
专门测了一下,将数组中小的开在前面比大的开在前面快得多,先循环数组前面得一维要比先循环后面一维快得多。