hdu2167 http://acm.hdu.edu.cn/showproblem.php?pid=2167
给定一个N*N的板子,里面有N*N个数字,选中一些数字,使得和最大
要求任意两个选中的数字不相邻,相邻包括上下,左右和对角线相邻。
由于N<=15,用程序判断了一下,每一行的有效状态<1600个,如果记录这些状态,然后每一行枚举当前行的上一行的状态那么极端下有1600*1600*15的复杂度,TLE
所以卡在这里很久,想不到怎么优化。 然后看了别人的代码知道了用邻接表存储哪两个状态是相容的。这样就不用枚举那么多了。所以就过了。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <sstream> 5 using namespace std; 6 int matrix[15][15]; 7 int dp[1<<15][15],situation[1600],bit[15],head[1000000],e; 8 struct node 9 { 10 int v,next; 11 }g[1000000];; 12 int max(const int &a, const int &b) 13 { 14 return a < b ? b : a; 15 } 16 17 void addEdge(int u, int v) 18 { 19 g[e].v = v; 20 g[e].next = head[u]; 21 head[u] = e++; 22 } 23 int count(int i, int ss, int n)//计算状体第i行的状态s所取的数的和 24 { 25 int ret = 0; 26 int j = 0; 27 for(j=0; j<n; ++j) 28 { 29 if(bit[j] & ss) 30 ret += matrix[i][j]; 31 } 32 return ret; 33 } 34 bool check(int s, int ss,int n)//检查s和ss是否可容 35 { 36 37 if(s&ss) return false; 38 if((s<<1)&ss) return false; 39 if((s>>1)&ss) return false; 40 return true; 41 } 42 int main() 43 { 44 int n,i,j,m,s,ss; 45 char str[100]; 46 bit[i=0] = 1; 47 for(i=1; i<15; ++i) 48 bit[i] = bit[i-1]<<1; 49 m = 1<<15; 50 for(i=0,j=0; i<m; ++i) 51 if(!(i&(i<<1)))//求出有效的状态 52 situation[j++]= i; 53 while(gets(str)) 54 { 55 memset(dp,0,sizeof(dp)); 56 e = 0; 57 memset(head,-1,sizeof(head)); 58 n = 0; 59 do 60 { 61 j = 0; 62 stringstream scin(str); 63 while(scin>>matrix[n][j]) j++; 64 n++; 65 gets(str); 66 if(str[0]=='\0') break; 67 68 }while(true); 69 m = 1<<n; 70 for(i=0;situation[i]<m; ++i)//判断哪两个状体相容,然后用邻接表存储 71 for(j=0; situation[j]<m; ++j) 72 if(check(situation[i],situation[j],n)) 73 addEdge(i,j); 74 for(i=0; situation[i]<m; ++i) 75 dp[situation[i]][0] = count(0,situation[i],n); 76 for(i=1; i<n; ++i) 77 for(s=0;situation[s]<m; ++s) 78 { 79 for(j=head[s];j!=-1;j=g[j].next) 80 { 81 dp[situation[g[j].v]][i] = max(dp[situation[g[j].v]][i],dp[situation[s]][i-1]+count(i,situation[g[j].v],n)); 82 } 83 } 84 int ans = 0; 85 for(i=0; situation[i]<m; ++i) 86 { 87 ans = max(ans,dp[situation[i]][n-1]); 88 } 89 printf("%d\n",ans); 90 } 91 return 0; 92 }
poj2411 http://poj.org/problem?id=2411
给定一个N*M的矩形,用1*2的矩阵填充满,问有多少种填充的方法。 N,M<=11
状态压缩dp,时间复杂度是N*(1<<M)*(1<<M),可以使用邻接表存储,哪些状态是相容的,这样子,就不用枚举所有的状态了。
状态是如何转移的呢?
首先第一行的状态必须是:如果有1,那么必须有连续的两个1.即不能有单独的一个1.
这样子是为了,第二行如果竖着放,那么就可以填充第一行的空缺。
怎么判断当前行的状态和上一行的状态不冲突的?详见bool check(int s, int ss)函数注释
1 #include <stdio.h> 2 #include <string.h> 3 #define LL __int64 4 int n,m; 5 int e,head[1000000]; 6 LL dp[1<<11][11]; 7 struct node 8 { 9 int v,next; 10 }g[1000000]; 11 12 void addEdge(int a, int b) 13 { 14 g[e].v = b; 15 g[e].next = head[a]; 16 head[a] = e++; 17 } 18 void swap(int &a, int &b) 19 { 20 int t = a; 21 a = b; 22 b = t; 23 } 24 bool check(int s) 25 { 26 while(s) 27 { 28 if( (s&1) && ((s>>1)&1)) 29 s>>=2; 30 else if( (s&1) && !((s>>1)&1)) 31 return false; 32 else if(!(s&1)) 33 s>>=1; 34 35 } 36 return true; 37 } 38 bool check(int s, int ss) 39 { 40 for(int i=0; i<m; ++i) 41 { 42 if(!(s&1) && !(ss&1)) return false;//上一行该位置为0,那么这一行该位置应该竖着放,否则不相容 43 else if(!(s&1)&&(ss&1)) 44 { 45 s>>=1;ss>>=1;continue;//上一行为0,这一行竖着放 46 } 47 else if((s&1)&&!(ss&1)) 48 { 49 s>>=1;ss>>=1;continue;//上一行该位置为1,这一行可以不放 50 } 51 else if((s&1)&&(ss&1))//上一行的该位置和这一行的该位置为1,那么这行的矩形是横着放 52 { 53 s>>=1,ss>>=1;i++; 54 if((s&1)&&(ss&1)) //即下一个位置,上一行和这一行都必须为1 55 { 56 s>>=1;ss>>=1;continue; 57 } 58 else return false; 59 } 60 } 61 return true; 62 } 63 int main() 64 { 65 int s,ss,t,i,j; 66 LL ans; 67 while(scanf("%d%d",&n,&m),n) 68 { 69 memset(dp,0,sizeof(dp)); 70 if(n<m) swap(n,m); 71 ans = 0; 72 t = 1<<m; 73 memset(head,-1,sizeof(head)); 74 e = 0; 75 for(s=0; s<t; ++s) 76 for(ss=0; ss<t; ++ss) 77 if(check(s,ss)) 78 addEdge(s,ss); 79 if((n*m)%2==0) 80 { 81 for(s=0; s<t; ++s) 82 if(check(s)) 83 dp[s][0] = 1; 84 for(i=1; i<n; ++i) 85 for(s=0; s<t; ++s) 86 for(j=head[s];j!=-1 && g[j].v<t;j=g[j].next) 87 dp[g[j].v][i] += dp[s][i-1]; 88 89 ans = dp[(1<<m)-1][n-1]; 90 } 91 printf("%I64d\n",ans); 92 } 93 return 0; 94 }