状态压缩模版2:选数
这是第二道模版题,然后的话这道模版题其实和上一题
这道题其实二进制的思想会体现的更加明显,因为的话我们一旦把我们需要的数转化成二进制之后,然后一个一个去判断,因为这种做法真的真的太神奇了,所以我觉得我讲不太通,就直接放代码吧
所以直接看代码吧
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 int f[16][1<<15];/*状态压缩,二进制*/ 9 int a[16][16],v[1<<15],vn,bin[16],n; 10 /*bin记录2的多少次方*/ 11 int main() 12 { 13 bin[1]=1;/*预处理,2的0次方*/ 14 for(int i=2;i<=15;i++) bin[i]=bin[i-1]<<1;/*预处理的就是2的多少次方*/ 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); 17 int maxx=(1<<n)-1;/*2^n-1,位运算*/ 18 vn=0;/*每一行里面有多少种满足题目条件的数*/ 19 memset(f,0,sizeof(f)); 20 for(int x=0;x<=maxx;x++)/*包括不能用的,我也只有这么多种,我们可以拆成2进制,*/ 21 /*比如(1代表被选中的) 22 00001011000 向左一位 23 00010110000 再&上面的,就是判断有没有重复的 24 如果有重复的话返回的就是1,没有就是0 25 00000010000 26 所以就证明这个有没有同时被选中 27 又比如说 28 00001001000 向左一位 29 00010010000 再&上面的,就是判断有没有重复的 30 00000000000 &后的结果 31 所以说明这两个是不相交了,这样我们就可以说明他们两个是没有同时被选中的 32 */ 33 { 34 if(((x<<1)&x)==0)/*判断相邻的两个有没有同时被选中*/ 35 { 36 for(int i=1;i<=n;i++)/*每一行有n个*/ 37 { 38 if(bin[i]&x) f[1][x]+=a[1][i]; 39 /*被选中的话,我们就把这个数的值加进f这个递归里面, 40 就是说x这个状态排列里面的总和是多少*/ 41 } 42 v[++vn]=x;/*这是一种合法情况,所以我们就加进去,下面可以for一遍*/ 43 } 44 } 45 for(int i=2;i<=n;i++)/*第一行已经处理完了,所以从第二行开始*/ 46 { 47 for(int p=1;p<=vn;p++)/*枚举每一种合法情况*/ 48 { 49 int tt=0; 50 for(int j=1;j<=n;j++) if(bin[j]&v[p]) tt+=a[i][j]; 51 /* 52 把这种情况的总和加进tt里面,如果我们选了这种状态并且这种状态可以满足: 53 1.相邻的两个没有同时被选中 54 2.其他的八个相邻的方向都不能选中 55 就加进去tt里面 56 */ 57 for(int q=1;q<=vn;q++)/*再for一遍,就是for我们的上一行是哪一种排列方式, 58 我们现在是第二行,所以我们这里就是第一行*/ 59 { 60 if((v[p]&v[q])==0 && (v[q]<<1&v[p])==0 && (v[q]>>1&v[p])==0) 61 /*1.上面的那个数满足条件 62 2,3.并且上面的那个数的相邻两个也是没有被选中过的*/ 63 f[i][v[p]]=max(f[i][v[p]],tt+f[i-1][v[q]]); 64 /*更新这个值,原来是f[i-1][v[q]]加上tt(现在这一行选中的数的值) 65 再和我之前的f[i][v[p]]比较哪个大*/ 66 } 67 } 68 } 69 int ans=0; 70 for(int i=1;i<=vn;i++) ans=max(ans,f[n][v[i]]); 71 printf("%d\n",ans); 72 return 0; 73 }
我们最终都要成长,最终都要和稚嫩的自己告别.