hlg2096---状态压缩dp
Corn Fields | ||||||
|
||||||
Description | ||||||
Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.
Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant. |
||||||
Input | ||||||
There are multiple test cases, please process to the end of file.
Line 1: Two space-separated integers: M and N Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile). |
||||||
Output | ||||||
For each test case: Line 1: One integer: the number of ways that FJ can choose the squares modulo 100 000 000. |
||||||
Sample Input | ||||||
2 3 1 1 1 0 1 0 |
||||||
Sample Output | ||||||
9 |
||||||
Hint | ||||||
Number the squares as follows: 1 2 3 4
There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9. Source |
||||||
Source | ||||||
USACO 2006 November Gold |
这是本人做的第一个状态压缩的题目,
题目大意:
给你一块土地,有的地方贫瘠有的地方肥沃,只有肥沃的土地能够种植植物,并且规定每个植物的四周不能种植植物
问最终有多少方案数?
分析:
给出的范围是:
《=12 因此考虑用状态压缩来做,
对于每一行的所用状态,首先判断该行是个否满足题目给的地图要求,
即肥沃与否的要求
对于每一个满足要求的,他与原图的&一定是他本身(1&1=1,其余都是0)
比如 土地的肥沃表示:101010011010
只要0对应的位置为0即可1对应的的位置不作要求
101000000000否和要求
00000000001便不符合要求
其次是要满足:和上一行的关系:即上一行种了的位置在该行不能种植
所以上一行与改行的&为0 保证上一行为1的话该行就为0;
这样就差不多了
事先处理一下第一行,
对,还有很重要的一点就是最后的结果:
这样考虑:
对于每一种状态,我们都是需找它的上一行的【兼容】状态,即能够通过上一行的那种状态转移到该状态;
所以上一种状态所保存的信息也可以通过该转移,到处理的状态;
有多上种状态能够转移到该状态,只要将所有能转移过来的状态上的信息求和便是该状态的信息;
这个题要求的是有多少种,所以,我们将状态定义成种数,对于第一行满足要求的状态我们赋值为1,表示该状态满足要求
对于2--n行:每个状态保存的是 有多少种方式能到该状态;
所以最后一行表示的是有多上种途径能到达该状态,也就是方案数;
所以将最后一行加起来就是最红的结果了;
值得注意的是:
每一行的0这种状态,即什么都不种植,这种转台是合法的,
代码:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 using namespace std; 6 7 const int maxn=12; 8 const int mod=100000000; 9 10 int map[maxn+2][maxn+2]; 11 int map_num[maxn+2]; 12 int dp[maxn+2][1<<maxn]; 13 14 bool check_raw(int num) 15 { 16 for(int i=1;i<=num;) 17 { 18 if((num&i)!=0&&(num&(i<<1))!=0) 19 return false; 20 i<<=1; 21 } 22 return true; 23 } 24 25 bool check_lie(int a,int b) 26 { 27 if((a&b)==0) 28 return true; 29 else 30 return false; 31 } 32 33 bool check_map(int num,int mp_num) 34 { 35 if((num&mp_num)==num) 36 return true; 37 else 38 return false; 39 } 40 41 int main() 42 { 43 int n,m; 44 // freopen("aa.txt","r",stdin); 45 while(scanf("%d %d",&n,&m)!=EOF) 46 { 47 for(int i=0;i<n;i++) 48 { 49 int sum=0; 50 int k=m-1; 51 for(int j=0;j<m;j++) 52 { 53 scanf("%d",&map[i][j]); 54 sum+=map[i][j]*(1<<k); 55 k--; 56 } 57 map_num[i]=sum; 58 } 59 int len=1<<m; 60 memset(dp,0,sizeof(dp)); 61 for(int j=0;j<len;j++) 62 { 63 if(check_raw(j) && check_map(j,map_num[0])) 64 { 65 dp[0][j]=1; 66 } 67 } 68 69 for(int i=1;i<n;i++) 70 { 71 for(int j=0;j<len;j++) 72 { 73 for(int k=0;k<len;k++) 74 { 75 if(dp[i-1][k] && check_raw(j) && check_map(j,map_num[i]) && check_lie(j,k)) 76 { 77 dp[i][j]+=dp[i-1][k]; 78 dp[i][j]%=mod; 79 80 } 81 } 82 } 83 } 84 int ans=0; 85 for(int j=0;j<len;j++) 86 { 87 ans+=dp[n-1][j]; 88 ans%=mod; 89 } 90 printf("%d\n",ans); 91 } 92 return 0; 93 }
用vector预处理了一下:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 using namespace std; 6 7 const int maxn=12; 8 const int mod=100000000; 9 10 int map[maxn+2][maxn+2]; 11 int map_num[maxn+2]; 12 int dp[maxn+2][1<<maxn]; 13 vector<int>v[1<<maxn]; 14 15 bool check_raw(int num) 16 { 17 for(int i=1;i<=num;) 18 { 19 if((num&i)!=0&&(num&(i<<1))!=0) 20 return false; 21 i<<=1; 22 } 23 return true; 24 } 25 26 bool check_lie(int a,int b) 27 { 28 if((a&b)==0) 29 return true; 30 else 31 return false; 32 } 33 34 bool check_map(int num,int mp_num) 35 { 36 if((num&mp_num)==num) 37 return true; 38 else 39 return false; 40 } 41 42 void init(int m) 43 { 44 int len=1<<m; 45 for(int i=0;i<len;i++) 46 v[i].clear(); 47 for(int i=0;i<len;i++) 48 { 49 if(check_raw(i)) 50 { 51 for(int j=0;j<len;j++) 52 { 53 if(check_raw(j)&&check_lie(i,j)) 54 { 55 v[i].push_back(j); 56 } 57 } 58 59 } 60 } 61 } 62 63 int main() 64 { 65 int n,m; 66 // freopen("aa.txt","r",stdin); 67 while(scanf("%d %d",&n,&m)!=EOF) 68 { 69 for(int i=0;i<n;i++) 70 { 71 int sum=0; 72 int k=m-1; 73 for(int j=0;j<m;j++) 74 { 75 scanf("%d",&map[i][j]); 76 sum+=map[i][j]*(1<<k); 77 k--; 78 } 79 map_num[i]=sum; 80 } 81 int len=1<<m; 82 memset(dp,0,sizeof(dp)); 83 init(m); 84 for(int j=0;j<len;j++) 85 { 86 if(check_raw(j) && check_map(j,map_num[0])) 87 { 88 dp[0][j]=1; 89 } 90 } 91 92 for(int i=1;i<n;i++) 93 { 94 for(int j=0;j<len;j++) 95 { 96 if(check_raw(j) && check_map(j,map_num[i])) 97 for(int k=0;k<v[j].size();k++) 98 { 99 int xx=v[j][k]; 100 if(dp[i-1][xx]) 101 { 102 dp[i][j]+=dp[i-1][xx]; 103 dp[i][j]%=mod; 104 105 } 106 } 107 } 108 } 109 int ans=0; 110 for(int j=0;j<len;j++) 111 { 112 ans+=dp[n-1][j]; 113 ans%=mod; 114 } 115 printf("%d\n",ans); 116 } 117 return 0; 118 }
今天对check_raw进行了优化:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <vector> 5 using namespace std; 6 7 const int maxn=12; 8 const int mod=100000000; 9 10 int map[maxn+2][maxn+2]; 11 int map_num[maxn+2]; 12 int dp[maxn+2][1<<maxn]; 13 vector<int>v[1<<maxn]; 14 15 bool check_raw(int num) 16 { 17 if((num&(num<<1))) 18 return false; 19 else 20 return true; 21 } 22 23 bool check_lie(int a,int b) 24 { 25 if((a&b)==0) 26 return true; 27 else 28 return false; 29 } 30 31 bool check_map(int num,int mp_num) 32 { 33 if((num&mp_num)==num) 34 return true; 35 else 36 return false; 37 } 38 39 void init(int m) 40 { 41 int len=1<<m; 42 for(int i=0;i<len;i++) 43 v[i].clear(); 44 for(int i=0;i<len;i++) 45 { 46 if(check_raw(i)) 47 { 48 for(int j=0;j<len;j++) 49 { 50 if(check_raw(j)&&check_lie(i,j)) 51 { 52 v[i].push_back(j); 53 } 54 } 55 56 } 57 } 58 } 59 60 int main() 61 { 62 int n,m; 63 // freopen("aa.txt","r",stdin); 64 while(scanf("%d %d",&n,&m)!=EOF) 65 { 66 for(int i=0;i<n;i++) 67 { 68 int sum=0; 69 int k=m-1; 70 for(int j=0;j<m;j++) 71 { 72 scanf("%d",&map[i][j]); 73 sum+=map[i][j]*(1<<k); 74 k--; 75 } 76 map_num[i]=sum; 77 } 78 int len=1<<m; 79 memset(dp,0,sizeof(dp)); 80 init(m); 81 for(int j=0;j<len;j++) 82 { 83 if(check_raw(j) && check_map(j,map_num[0])) 84 { 85 dp[0][j]=1; 86 } 87 } 88 89 for(int i=1;i<n;i++) 90 { 91 for(int j=0;j<len;j++) 92 { 93 if(check_raw(j) && check_map(j,map_num[i])) 94 for(int k=0;k<v[j].size();k++) 95 { 96 int xx=v[j][k]; 97 if(dp[i-1][xx]) 98 { 99 dp[i][j]+=dp[i-1][xx]; 100 dp[i][j]%=mod; 101 102 } 103 } 104 } 105 } 106 int ans=0; 107 for(int j=0;j<len;j++) 108 { 109 ans+=dp[n-1][j]; 110 ans%=mod; 111 } 112 printf("%d\n",ans); 113 } 114 return 0; 115 }