hlg2096---状态压缩dp

Corn Fields
Time Limit: 2000 MSMemory Limit: 65536 K
Total Submit: 9(4 users)Total Accepted: 5(4 users)Rating: Special Judge: No
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 }
View Code

   

用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 }
View Code

 

今天对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 }
View Code

 

转载于:https://www.cnblogs.com/zhanzhao/p/3648427.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值