hdu 1565 方格取数(1) 状态压缩dp
方格取数(1)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3848 Accepted Submission(s): 1473
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3
75 15 21
75 15 28
34 70 5
Sample Output
188
Author
ailyanlu
超内存,用滚动数组了,超时!
算一下时间复杂度 20*20*2^20*2^20 额额。。。。
超时代码留个标记
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 using namespace std; 6 7 int a[21][21]; 8 int dp1[1<<21],before[1<<21],now[1<<21]; 9 10 void prepare(int i,int n)//预处理 11 { 12 int j,k,s; 13 k=1<<n; 14 for(j=0; j<k; j++) 15 { 16 dp1[j]=0; 17 for(s=1; s<=n; s++) 18 { 19 if( (j&(1<<(s-1))) == (1<<(s-1)) )//包含了这个数字 20 dp1[j]=dp1[j]+a[i][s]; 21 } 22 } 23 } 24 bool panduan(int b,int n) 25 { 26 int i,x; 27 x=3; 28 for(i=1; i<n; i++) 29 { 30 if( (x&b) == x) return false; 31 x=x<<1; 32 } 33 return true; 34 } 35 int main() 36 { 37 int n; 38 int i,j,s,k,hxl; 39 scanf("%d",&n); 40 { 41 for(i=1; i<=n; i++) 42 for(j=1; j<=n; j++) 43 scanf("%d",&a[i][j]); 44 memset(now,0,sizeof(now)); 45 memset(before,0,sizeof(before)); 46 k=1<<n; 47 for(i=1; i<=n; i++) 48 { 49 prepare(i,n); 50 for(j=0; j<k; j++) 51 if(panduan(j,n)) 52 { 53 hxl=0; 54 for(s=0; s<k; s++) 55 { 56 if( (s&j)>0 ) continue; 57 if(before[s]>hxl) 58 hxl=before[s]; 59 } 60 dp1[j]=dp1[j]+hxl; 61 } 62 for(j=0; j<k; j++) 63 { 64 now[j]=dp1[j]; 65 before[j]=now[j]; 66 } 67 } 68 hxl=0; 69 for(i=0; i<k; i++) 70 if(now[i]>hxl) hxl=now[i]; 71 72 printf("%d\n",hxl); 73 } 74 return 0; 75 }
优化:
很多地方可以优化。
1.其实,可以预处理相邻的情况。不要每一次都判断。就像打表一样。//怎么处理的呢?具体看一下代码。
2.预处理相邻的情况后,发现,1<<20的数组,满足条件的只有17711种,这样的话,优化就巨大了。
3.滚动数组。
我的代码,还是不够优化的,960ms
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 int a[22][22]; 6 int state[17715],len; 7 int dp[1<<22],befor[1<<22]; 8 9 void make_init()//预处理,所以的状态 10 { 11 int i,k; 12 k=1<<20; 13 len=0; 14 for(i=0;i<k;i++) 15 { 16 if( (i&(i<<1))>0 ) continue; 17 else state[len++]=i; 18 } 19 } 20 void prepare(int r,int n)//处理每一行的dp值. 21 { 22 int i,j,k,ans,x; 23 k=1<<n; 24 25 for(j=0;state[j]<k;j++) 26 { 27 i=state[j]; 28 dp[i]=0; 29 x=i; 30 ans=1; 31 while(x) 32 { 33 if(x&1) dp[i]=dp[i]+a[r][ans]; 34 ans++; 35 x=x>>1; 36 } 37 } 38 } 39 40 int main() 41 { 42 int n; 43 int i,j,k,s,hxl; 44 make_init(); 45 while(scanf("%d",&n)>0) 46 { 47 for(i=1;i<=n;i++) 48 for(j=1;j<=n;j++) 49 scanf("%d",&a[i][j]); 50 51 k=1<<n; 52 memset(befor,0,sizeof(befor)); 53 for(i=1;i<=n;i++) //枚举每一行 54 { 55 prepare(i,n); 56 for(j=0;state[j]<k;j++)//枚举每一种 有效 状态 57 { 58 hxl=0; 59 for(s=0;state[s]<k;s++)//上一行的有效状态 60 { 61 if( (state[j]&state[s]) >0) continue;//没有冲突 62 if(befor[state[s]]>hxl) 63 hxl=befor[state[s]]; 64 } 65 dp[state[j]]=dp[state[j]]+hxl; 66 } 67 for(j=0;state[j]<k;j++) 68 { 69 befor[state[j]]=dp[state[j]]; 70 } 71 } 72 hxl=0; 73 for(i=0;state[i]<k;i++) 74 if(hxl<befor[state[i]]) hxl=befor[state[i]]; 75 printf("%d\n",hxl); 76 } 77 return 0; 78 }
进一步的优化:
看了一下,别人的博客,看到一个很别人开的数组是很小的。why?
我开的dp数组是 dp[1<<22],其实只要开dp[17715];和状态state[17715 ]一样就可以了.
为什么可以呢?
简单说明一下:原来的方法,是和最原始的位置是一一对应,所以,很好理解。
但是,有很多的空间是浪费的。
现在是对应状态一一对应,就是这样。这样数组开的就小很多了。而且速度快一些。
921ms --> 486 ms
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 int a[22][22]; 6 int state[17715],len; 7 int dp[17715],befor[17715]; 8 9 void make_init()//预处理,所以的状态 10 { 11 int i,k; 12 k=1<<20; 13 len=0; 14 for(i=0;i<k;i++) 15 { 16 if( (i&(i<<1))>0 ) continue; 17 else state[len++]=i; 18 } 19 } 20 void prepare(int r,int n)//处理每一行的dp值. 21 { 22 int j,k,ans,x; 23 k=1<<n; 24 25 memset(dp,0,sizeof(dp)); 26 for(j=0;state[j]<k;j++) 27 { 28 x=state[j]; 29 ans=1; 30 while(x) 31 { 32 if(x&1) dp[j]=dp[j]+a[r][ans];//!!!哈哈。 33 ans++; 34 x=x>>1; 35 } 36 } 37 } 38 39 int main() 40 { 41 int n; 42 int i,j,k,s,hxl; 43 make_init(); 44 while(scanf("%d",&n)>0) 45 { 46 for(i=1;i<=n;i++) 47 for(j=1;j<=n;j++) 48 scanf("%d",&a[i][j]); 49 50 k=1<<n; 51 memset(befor,0,sizeof(befor)); 52 for(i=1;i<=n;i++) //枚举每一行 53 { 54 prepare(i,n); 55 for(j=0;state[j]<k;j++)//枚举每一种 有效 状态 56 { 57 hxl=0; 58 for(s=0;state[s]<k;s++)//上一行的有效状态 59 { 60 if( (state[j]&state[s]) >0) continue;//没有冲突 61 if(befor[s]>hxl) 62 hxl=befor[s]; 63 } 64 dp[j]=dp[j]+hxl; 65 } 66 for(j=0;state[j]<k;j++) 67 { 68 befor[j]=dp[j]; 69 } 70 } 71 hxl=0; 72 for(i=0;state[i]<k;i++) 73 if(hxl<befor[i]) hxl=befor[i]; 74 printf("%d\n",hxl); 75 } 76 return 0; 77 }