我的第一篇微博*-*,状态压缩入门POJ3254

这是我的第一篇微博,好兆头!


前面是废话啊,之前做了不少01背包,完全背包,多重背包的题目,也算是对动态规划有了初步的了解,动态规划的关键是找到当前状态与前一状态或后一状态之间的关系,以写出合适的状态转移方程。

状态压缩用在需要表示的状态比较多的时候,状态压缩灵活地运用了二进制的位的特点表示了一种状态。其他更加深入的大家可以看这篇微博->状态压缩DP入门 –

下面附上POJ3254原题

Corn Fields

Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 11536
Accepted: 6045

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

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

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


题解

题目简单解释一下:手动输入n*m大小的农场,1表示该处是肥沃的土地,0表示该处比较荒不能种庄稼,农民想在农场上种玉米给牛吃,限制两点:1.标号为0处不能种玉米,2.相邻的两处地不能种玉米(得隔着种);
问:农民共有多少种种玉米的方法(一块地也不种也算一种方法)。结果对100000000取膜。

列:输入农场
1 1 1
0 1 0
农民可以种玉米的方法有:(1表示该处种玉米,0表示该处不种)

1 .种0块地:
0 0 0
0 0 0
计1种方法

2 .种1块地:
1 0 0 || 0 1 0 || 0 0 1 || 0 0 0
0 0 0 || 0 0 0 || 0 0 0 || 0 1 0
计4种方法

2.种2块地
1 0 1 || 1 0 0 || 0 0 1
0 0 0 || 0 1 0 || 0 1 0
计3种方法

3.种3块地
1 0 1
0 1 0
计1种方法

故,综上所述共计1+4+3+1=9种方法
输入: 9

思路

刚看到这题目,我的第一想法就是用一个二维数组a[m][n]来存放农村地图,可是这样的话一个二维数组点a[i][j]仅表示该点处的具体信息(能否种庄稼),这样用状态DP遍历所有的情况就会很麻烦,其实一个数字就能表示一行农场的土地情况了,那就是用整数的二进制表示。

例如:

例子里面第一行土地的情况是: 1 1 1 这刚好与数字“7”的二进制吻合:1*2^0+1*2^1+1*2^2=7; 第二行为:0 1
0,即可以用数字“2”表示

在遍历第一行的情况时就可以从 0 开始,每次加1,直到等于2^(列数)-1,当然,这里面有错误的情况,需要用一个子函数判错;
则用dp[i][j]表示在第i行种植j情况(若j为2,情况为0 1 0;若j为3,情况为0 1
1)的前提下之前所有i-1行中能够匹配的方法之和,则得状态转移方程:

dp[i][j]=dp[i-1][k]

( k为前一行与j情况兼容的种植方法,即相邻位置不种玉米)
则需要三个for循环,第一个从1到m(列数),第二个从0到2^n-1,遍历第i行的所有情况,第三个同样的为从0到2^n-1,遍历i-1行的所有情况。
其中判定当前情况能否存在的方法是
1. 用当前情况与这一行土地情况进行或(|)运算,若结果仍为该行土地情况,则符合条件;
2. 将当前情况向左移一位,再与原来情况进行和(&)运算,若结果为零,则符合条件;

,下面上代码:


//Li Wenjun
#include
#include
#include
#include 
#include 
using namespace std;
#define mod 100000000;
int dp[13][(1<<13)],a[15];
int M,N;
int judge(int j,int k)
{
    if((j|k)!=k)
        return 1;
    if(((j<<1)&j)!=0)
        return 1;
    return 0;
}
int StatePress()
{
    dp[0][0]=1;
    for(int i=1;i<=M;i++)
    {
         for(int j=0;j<(1<if(judge(j,a[i])) //排除不符合的情况
            continue;
            for(int k=0;k<(1<if((j&k)==0)   此处前一行也可以再加一行判定的
                {
                    dp[i][j]+=dp[i-1][k];
                    dp[i][j]%=mod;
                }
            }
        }
    }
    return 0;
}
int main()
{
 //   freopen("in.txt","r",stdin);此处是检验结果用的
    int k;
    cin>>M>>N;
    for(int i=1;i<=M;i++)
    {
        a[i]=0;
        for(int j=1;j<=N;j++) //将土地情况转化为二进制
        {
            cin>>k;
            a[i]=(a[i]<<1)+k;
        }
    }
    memset(dp,0,sizeof(dp));  // 数组置零
    StatePress();
    int total=0;
    for(int i=0;i<(1<cout<return 0;
}

程序还有许多可以优化的地方,欢迎提意见:-)

你可能感兴趣的:(acm例题)