[状压动规] [luoguP1879] [USACO06NOV] 玉米田Corn Fields

题目描述

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.

农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

 

输入输出格式

输入格式:

第一行:两个整数M和N,用空格隔开。

第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。

输出格式:

一个整数,即牧场分配总方案数除以100,000,000的余数。

输入输出样例

输入样例#1:
2 3
1 1 1
0 1 0
输出样例#1:
9

俺的题解

 状态压缩入门题!终于搞懂了。

 草地的状态无法用数组表示,最好的方式就是把状态压缩成01表示的二进制数。

 这就需要用到位运算

  & 只有1&1 = 1,其他都等于0;

  | 有一个是1,结果就为1;

  ^ 不同为1,相同为0,常用取反;

  ’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充,x<<2相当于让x乘以4。

  相应的,’>>’是右移操作,x>>1相当于给x/2,去掉x二进制下的最右一位。

  

  这四种运算在状压dp中有着广泛的应用,常见的应用如下:

  1.判断一个数字x二进制下第i位是不是等于1。

    方法:if ( ( ( 1 << ( i - 1 ) ) & x ) > 0)

    将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。

  2.将一个数字x二进制下第i位更改成1。

    方法:x = x | ( 1<<(i-1) )

    证明方法与1类似,此处不再重复证明。

  3.把一个数字二进制下最靠右的第一个1去掉。

    方法:x=x&(x-1)

 本题中

 map数组中存储每行土地是否贫瘠的状态,和输入的01反过来,方便后面位运算判断。

 state数组枚举所有不相邻草地方案的状态,0表示没有草地,1表示有。

 状态转移方程  dp[本行][本行草地状态] = sigma(dp[上一行][上一行草地状态] ) 上一行草地状态满足不能与本行的草地相邻。

 

俺的代码

 1 /*
 2 作者:Monkey_TU
 3 */
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstring>
 7 using namespace std;
 8 
 9 const int mod = 100000000;
10 int n, m;
11 int map[15], dp[15][1<<15];
12 
13 int main()
14 {
15     memset(dp, 0, sizeof(dp));
16     memset(map, 0, sizeof(map));
17     scanf("%d%d", &n, &m);
18     for(int i = 1; i <= n; i++)
19     {
20         for(int j = 1; j <= m; j++)
21         {
22             int p;
23             scanf("%d", &p);
24             map[i] = (map[i] << 1) + (p ^ 1);//反着存,方便后面判断 
25         }
26     }
27     //第一行dp 
28     int cnt = 0, state[1<<15];
29     for(int i = 0; i < (1<<m); i++)
30     {
31         if(! (i & i<<1))//不相邻状态 
32             state[++cnt] = i;
33     }
34     for(int i = 1; i <= cnt; i++)
35     {
36         if(! (map[1] & state[i]))
37         {
38             dp[1][state[i]] = 1;
39         }
40     }
41     
42     for(int i = 2; i <= n; i++)
43     {
44         for(int j = 1; j <= cnt; j++)
45         {
46             if(! (map[i] & state[j])) 
47             {
48                 for(int k = 1; k <= cnt; k++)
49                 {
50                     if(! (map[i-1] & state[k]))
51                     {
52                         if( !(state[j] & state[k]))
53                         {
54                             dp[i][state[j]] = (dp[i][state[j]]%mod + dp[i-1][state[k]]%mod) % mod;
55                         }
56                     }
57                 }
58             }
59         }
60     }
61     int ans = 0;
62     for(int i = 1; i <= cnt; i++)
63     {
64         ans = (ans + dp[n][state[i]])%mod;
65     }
66     
67     printf("%d\n", ans);
68     return 0;
69 }
点击就送屠龙宝刀

 

俺的反思

 SB错误:ans求余数的时候没有整体取模,而是只对加数进行了取模运算,导致结果少取模了一次。

 

 

posted @ 2017-09-11 12:52  秃猴  阅读(183)  评论(0编辑  收藏  举报