HDU1565方格取数

 典型的状态压缩DP问题。第i行的取法只受到第i-1行的影响。首先每一行的取法要相容(不能有两个相邻),然后相邻行之间也要相容。将每一个格子看做两种状态,1表示取,0表示不取。这样每一行就是一个01串,恰好可以看做是一个二进制数,那么该二进制数对应的十进制整数可以唯一的表示为当前第 i 行的状态。定义用dp[i][j]表示前 i 行状态为j 时最大和。其中状态 j对应的整数为stu[j],数组stu[]收录的是所有合法的状态,所谓合法状态是:若一个整数的二进制中没有任意两个1相邻,那么该整数就是合法的状态。判定方法:若 x&(x<<1)==0,则x是合法的状态。状态转移方程如下:

                    dp[i][j]=max{dp[i-1][w]}+value[i][j] (w&j==0) ;

方程含义:j是第i行状态,w是第i-1行的状态,value[i][j]是第i行状态为j时第i行取法的和。从w状态转移到j状态需要满足w&j==0(他们是相容的)。计算时候需要枚举第i行所有状态w。代码如下:

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
using namespace std;
#define MAX_SIZE 17712                //当n=20时可以计算得到最多有11710中状态
int stu[MAX_SIZE], Value[MAX_SIZE];    //stu表示总的状态数,MAX_SIZE表示对应状态的值
int a[22][22], dp[22][MAX_SIZE];
int initialization(int n);
int stuValue(int i, int j);
int main(){
    int i, j, k, m, n, lmax, maxSum;
    while (~scanf("%d", &n)){
        m = initialization(n);
        for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
            scanf("%d", &a[i][j]);
        memset(dp[0], 0, sizeof(dp[0]));  
        maxSum = 0;
        for (i = 1; i <= n; i++)
        for (j = 0; j < m; j++){
            lmax = 0;
            for (k = 0; k < m; k++){
                if (!(stu[j] & stu[k]) && lmax < dp[i - 1][k])  //如果上下相容,且当前的值大于原来最大值
                    lmax = dp[i - 1][k];
            }
            dp[i][j] = lmax + stuValue(i, j);     //计算状态dp[i][j]
            if (maxSum < dp[i][j])
                maxSum = dp[i][j];
        }
        printf("%d\n", maxSum);
    }
    return 0;
}
int initialization(int n){
    int ant = 0;
    for (int i = 0; i < (1 << n); i++){
        if (!(i&(i << 1)))
            stu[ant++] = i;
    }
    return ant;
}
int stuValue(int i, int j){   //第i行,状态为stu[j]时候第i行的值
    int k = stu[j], t = 1, sum = 0;
    while (k>0){
        if (k & 1)
            sum += a[i][t];
        t++;
        k = k >> 1;
    }
    return sum;
}

 

posted @ 2016-04-28 18:59  曹孟德  阅读(283)  评论(0编辑  收藏  举报