HDU 2167 Pebbles

Problem Description

You're given an unlimited number of pebbles to distributeacross an N x N game board (N drawn from [3, 15]), where each square on theboard contains some positive point value between 10 and 99, inclusive. A 6 x 6board might look like this:
                             

The player distributes pebbles across the board so that:

?At most one pebble resides in any given square.
?No two pebbles are placed on adjacent squares. Two squares are consideredadjacent if they are horizontal, vertical, or even diagonal neighbors. There'sno board wrap, so 44 and 61 of row three aren't neighbors. Neither are 33 and75 nor 55 and 92.

The goal is to maximize the number of points claimed by your placement ofpebbles.

Write a program that reads in a sequence of boards from an input file andprints to stdout the maximum number of points attainable by an optimal pebbleplacement for each. 

 

 

Input

Each board is expressed as a series of lines, where eachline is a space-delimited series of numbers. A blank line marks the end of eachboard (including the last one)

 

 

Output

then your program would print the maximum number ofpoints one can get by optimally distributing pebbles while respecting the tworules, which would be this (each output should be printed on a single line andfollowed with a newline):

 

 

Sample Input

71 24 95 56 54

85 50 74 94 28

92 96 23 71 10

23 61 31 30 46

64 33 32 95 89

 

78 78 11 55 20 11

98 54 81 43 39 97

12 15 79 99 58 10

13 79 83 65 34 17

85 59 61 12 58 97

40 63 97 85 66 90

 

33 49 78 79 30 16 34 88 54 39 26

80 21 32 71 89 63 39 52 90 14 89

49 66 33 19 45 61 31 29 84 98 58

36 53 35 33 88 90 19 23 76 23 76

77 27 25 42 70 36 35 91 17 79 43

33 85 33 59 47 46 63 75 98 96 55

75 88 10 57 85 71 34 10 59 84 45

29 34 43 46 75 28 47 63 48 16 19

62 57 91 85 89 70 80 30 19 38 14

61 35 36 20 38 18 89 64 63 88 83

45 46 89 53 83 59 48 45 87 98 21

 

15 95 24 35 79 35 55 66 91 95 86 87

94 15 84 42 88 83 64 50 22 99 13 32

85 12 43 39 41 23 35 97 54 98 18 85

84 61 77 96 49 38 75 95 16 71 22 14

18 72 97 94 43 18 59 78 33 80 68 59

26 94 78 87 78 92 59 83 26 88 91 91

34 84 53 98 83 49 60 11 55 17 51 75

29 80 14 79 15 18 94 39 69 24 93 41

66 64 88 82 21 56 16 41 57 74 51 79

49 15 59 21 37 27 78 41 38 82 19 62

54 91 47 29 38 67 52 92 81 99 11 27

31 62 32 97 42 93 43 79 88 44 54 48

 

 

Sample Output

572

683

2096

2755

题目大意:给一个N*N的矩阵,矩阵每个位置都存一个二位数。现在选择矩阵里的一些点,使得所选点之和最大。选点的原则是,选了一个点之后,它周围的八个点都不能再选了。问最大值是多少。

 

就是一个状态压缩的dp。将每行合理的取点情况先保存在way里面,ans[i][0]为对应的是每种方法取的个数。ans[i][1~k]为这种情况取点的坐标(k=ans[i][0])。s[i][j]存的是第i行在第j种情况下的和是多少。

dp[i][j]的意义:第i行放第j种情况的最大值。

先确定第0行的dp值,dp[0][i]就为s[0][i];因为每个点(i,j)只会影响下面一行。与下面一行(i+1,j-1),(i+1,j),(i+1,j+1)冲突,所以只要下面放置的情况跟上一行满足!(way[j]&way[k]) &&!((way[j]<<1)&way[k])&&!((way[j]>>1)&way[k])即可。

状态转移方程为dp[i][j] = max(dp[i][j],dp[i-1][k] + s[i][j]);k和j必须要满足条件。

 

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

char ch[200];
int num[20][20] ,n ,way[1600] ,dp[16][1610] ,ans[1610][17] ,s[20][1610];

int main()
{
    int len;
    while(gets(ch))
    {
        len = strlen(ch);
        if(len==0)
        {
            break;
        }
        n = 0;
        for(int i = 0;i<len;i+=3)
        {
            num[0][n++] = (ch[i] - '0') * 10 + (ch[i+1] - '0');
        }
        for(int i = 1;i<n;i++)
        {
            for(int j = 0;j<n;j++)
            {
                scanf("%d",&num[i][j]);
            }
        }
        getchar();
        getchar();
        int temp ,cnt = 0 ,cc;
        memset(ans,0,sizeof(ans));
        for(int i = 0;i<1<<n;i++)
        {temp = i;
            if((i<<1)&i)
            {
                continue;
            }
            else
            {
                way[cnt] = i;
                cc = 0;
                while(temp > 0)
                {
                    if(temp&1)
                    {
                        ans[cnt][0]++;
                        ans[cnt][ans[cnt][0]] = cc;
                    }
                    cc++;
                    temp >>= 1;
                }
                cnt++;
            }
        }
        int sum;
        for(int i = 0;i<cnt;i++)
        {
            for(int j = 0;j<n;j++)
            {
                sum = 0;
                for(int k = 1;k<=ans[i][0];k++)
                {
                    sum += num[j][ans[i][k]];
                }
                s[j][i] = sum;
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i = 0;i<cnt;i++)
        {
            dp[0][i] = s[0][i];
        }
        for(int i = 1;i < n;i++)
        {
            for(int j = 0;j<cnt;j++)
            {
                for(int k = 0;k<cnt;k++)
                {
                    if((way[j]&way[k]) ||((way[j]<<1)&way[k]) || ((way[j]>>1)&way[k]))
                    {
                        continue;
                    }
                    dp[i][j] = max(dp[i][j] ,dp[i-1][k] + s[i][j]);
                }
            }
        }
        int m = 0;
        for(int i = 0;i<cnt;i++)
        {
            if(m < dp[n-1][i])
            {
                m = dp[n-1][i];
            }
        }
        printf("%d\n",m);
    }
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值