HDOJ/HDU 1565 方格取数(1)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1565

  题目意思就是说给出一个n*n的棋盘,每个位置上有一个非负整数,在上面取不相邻的若干个数,使得和最大。

  解法:位运算+dp

  因为每行可以取的位置都是一样的,只要两个数不相邻即可。所以可以先对1<<n以内处理,筛除不合法的,优化效率,然后对每行的每一个状态,在上一行合法而且跟本状态不冲突的所有状态中找一个和最大的。本行的本状态的值就是这一行在这个状态下取得的所有数的和+这个最大值。如果是第一行,则上一行的最大值为0;

  需要注意的一个地方就是n可能为0,(>_<)我看了discuss才知道这么个变态的情况,如果为0直接输出0即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <map>
#include <cstring>
#include <string.h>
#include <fstream>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;

int d[1<<20], k, n, a[25][25];
__int64 sum[25][1<<20];

void init(int n){
    k = 0;
    int j;
    for (j = 0; j < 1<<n; j++){
        if (j&(j<<1) || j&(j>>1))
            continue;
        d[k++] = j;
    }
}

__int64 get_sum(int i, int j)
{
    int l = n;
    __int64 tot = 0;
    while (j > 0){
        if (j % 2)
            tot += a[i][l];
        j /= 2;
        l--;
    }
    return tot;
}

int main(){

    int i, j, l;

    while (scanf("%d", &n) != EOF){

        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++)
                scanf("%I64d", &a[i][j]);
        init(n);
        for (i = 1; i <= n; i++)
        {
            for (j = 0; j < k; j++)
            {
                if (i == 1){
                    sum[i][j] = get_sum(i, d[j]);
                    continue;
                }
                __int64 temp = -1;
                for (l = 0; l < k; l++){
                    if (d[j] & d[l])continue;
                    temp = max(temp, sum[i-1][l]);
                }
                sum[i][j] = temp + get_sum(i, d[j]);
            }
        }

        __int64 maxf = -1;
        for (i = 1; i <= n; i++)
            for (j = 0; j < k; j++)
                maxf = max(maxf, sum[i][j]);

        printf("%I64d\n", maxf);
    }
    return 0;
}
posted @ 2011-08-24 09:33  like@neu  阅读(283)  评论(0)    收藏  举报