HDU 2167 Pebbles(状压DP)

题目链接:Pebbles

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1504    Accepted Submission(s): 865

Problem Description

You‘re given an unlimited number of pebbles to distribute across an N x N game board (N drawn from [3, 15]), where each square on the board contains some positive point value between 10 and 99, inclusive. A 6 x 6 board 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 considered adjacent if they are horizontal, vertical, or even diagonal neighbors. There‘s no board wrap, so 44 and 61 of row three aren‘t neighbors. Neither are 33 and 75 nor 55 and 92.

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

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

Input

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

Output

then your program would print the maximum number of points one can get by optimally distributing pebbles while respecting the two rules, which would be this (each output should be printed on a single line and followed with a newline):

题解:代码虽然长,却是我调了四个小时调出来的。QAQ

二进制表示每一行的状态,初始化记录一整行每一个状态的值,用cnt[i][j]表示,j表示这一行的状态。然后dp开始转移,对于第 i 行,枚举每一个状态j,对于状态j,首先分析j是否满足相邻的最多有一个石子,然后考虑 i-1 位,分析i-1可能的状态,dfs一下寻找i-1行的最大值maxx,所以dp[i][j]  = maxx + cnt[i][j].

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int dp[17][1<<17];
int cnt[17][1<<17];
int a[17][17],n;
char s[50];
int vis[20];
int maxx = 0;
void dfs(int i,int now,int state,int sumstate)
{
    maxx = max(maxx,dp[i][sumstate]);
    if(vis[now]==0)
    {
        if(now<n-1)
        {
            dfs(i,now+1,1,sumstate);
        }
        else
        {
            return;
        }
    }
    else if(vis[now]==1)
    {
        if(now>=n-1&&state==1) dfs(i,now+1,0,sumstate+(1<<now));
        else if(now>=n-1&&state==0) return ;
        else if(now<n-1&&state==1)
        {
            dfs(i,now+1,0,sumstate+(1<<now));
            dfs(i,now+1,1,sumstate);
        }
        else if(now<n-1&&state==0)
        {
            dfs(i,now+1,1,sumstate);
        }
    }
    return;
}
int cal(int c1,int c2)
{
    return ((c1-‘0‘)*10+c2-‘0‘);
}
bool judge(int state)   //判断此状态是否符合
{
    for(int i=0;i<n-1;i++)
    {
        if(((1<<i)&state)>0&&((1<<(i+1))&state)>0) return 0;
    }
    return 1;
}
int main()
{
    while(gets(s))
    {
        int len = strlen(s);
        n = (len+1)/3;
        int ans = 0;
        for(int i=0;i<len;i+=3)
        {
            a[0][ans++] = cal(s[i],s[i+1]);
        }
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        for(int i=0;i<n;i++)      //预处理记录cnt[i][j]
        {
            for(int j=0;j<(1<<(n));j++)
            {
                int sum = 0;
                for(int k=0;k<n;k++)
                {
                    if(((1<<k)&j)>0)
                    {
                        sum += a[i][k];
                    }
                }
                cnt[i][j] = sum;
            }
        }
        for(int j=0;j<(1<<n);j++) dp[0][j] = cnt[0][j];
        for(int i=1;i<n;i++)             //枚举每一行,枚举每个状态,判断可行性。
        {
            for(int j=0;j<(1<<n);j++)
            {
                if(!judge(j)) continue;
                memset(vis,0,sizeof(vis));
                for(int k=0;k<n;k++)
                {
                    if(k==0)
                    {
                        if(((1<<k)&j)==0&&((1<<(k+1))&j)==0)
                        {
                            vis[k] = 1;
                        }
                    }
                    else if(k==n-1)
                    {
                        if(((1<<k)&j)==0&&((1<<(k-1))&j)==0)
                        {
                            vis[k] = 1;
                        }
                    }
                    else
                    {
                        if(((1<<k)&j)==0&&((1<<(k-1))&j)==0&&((1<<(k+1))&j)==0)
                        {
                            vis[k] = 1;
                        }
                    }
                }
                maxx = 0;   //dfs寻找i-1行的最大值
                if(vis[0]==0)
                dfs(i-1,0,0,0);
                else
                {
                    dfs(i-1,0,1,0);
                    dfs(i-1,0,0,0);
                }
                dp[i][j] = maxx+cnt[i][j];
            }
        }
        getchar();
        gets(s);
        int maxn = 0;
        for(int i=0;i<(1<<n);i++) maxn = max(maxn,dp[n-1][i]);
        printf("%d\n",maxn);
        memset(dp,0,sizeof(dp));
    }
    return 0;
}
/*
10 20 30
10 20 30
10 20 30
*/
时间: 2024-08-10 13:38:17

HDU 2167 Pebbles(状压DP)的相关文章

HDU 4284Travel(状压DP)

HDU 4284    Travel 有N个城市,M条边和H个这个人(PP)必须要去的城市,在每个城市里他都必须要“打工”,打工需要花费Di,可以挣到Ci,每条边有一个花费,现在求PP可不可以从起点1走完所有的他必须要去的城市,打完所有的工,并且成功回到起点1 由于H<=15,所以显然可以状压,预处理这些都必须去的城市之间的最短距离(可以floyd),然后状压DP[S][i]表示他到达了S这些城市后正处在第i个城市里(所以S & (1<<i) != 0)的剩余的最大的钱的数量,然

HDU 3001 Travelling 状压DP

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001 题意:还是环游地图的问题,只不过这回旅行者对自己有着严格的要求,地图上每个点的经过次数不能超过两次. 思路:依然是状压DP问题,根上一道很像,只不过这次对于每个点来说有三种状态,分别是未经过,经过一次,经过两次.所以要用三进制的数来进行状态压缩,这个关键点想明白了其他的和上一道基本一样了.对于我来说需要注意的是:能够到达某一个点经过了两次的状态的前一个状态是这个点已经经过了一次的状态,而不是从来未

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

HDU 3681 BFS&amp;状压DP&amp;二分

N*M矩阵,从F点出发,走完所有的Y点,每走一格花费1点电量,走到G点时,电量充满,D不可到达,问起始时的最小满电量可以走完所有Y,Y和G一共最多15个 先BFS出所有的F,Y,G之间的最短距离. 然后二分起始电量,对每个电量,做状压DP判断是否可行 #include "stdio.h" #include "string.h" #include "queue" using namespace std; int inf=0x3f3f3f3f; in

LianLianKan HDU - 4272(状压dp)

题意:就是连连看,有两个相同的就能消除,再加上两个特别的规定,一是只能从栈顶开始消除,而是两个相同的元素之间距离不能超过6,询问能否消除序列中所有元素. 思路:数据水,贪心就能过,但严谨的考虑,贪心显然不能解决所有问题.这题虽然序列很长,但是状态并不复杂,可以使用滚动的状压dp,然后考虑使用多少位表示状态,每一种状态表示第i位为栈首的序列状态,该位前最多能消除掉该位后四位,所以我们只需要表示后9位的状态即可,总计10位. 某位上1表示该位已被消除,0表示未被消除. dp[i][j]表示从i位开始

方格取数(1) HDU - 1565 (状压dp)

给你一个n*n的格子的棋盘,每个格子里面有一个非负数.     从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20) Output对于每个测试实例,输出可能取得的最大的和 Sample Input 3 75 15 21 75 15 28 34 70 5 Sample Output 188 思路:状压dp,建立dp[i][j]二维数组,表示第i行状

hdu 2825 (Aho-Corasick &amp; 状压DP) - xgtao -

题目链接 给出m(m<=10)个模板串,问长度为n(n<=25)的字符串包含至少k个模板串的种类数,对20090717取模. 套路,有多个模板串考虑Aho-Corasick,因为想了很久找不到什么可行办法做,就考虑Dp,因为m<=10,所以可以有状压来检查当前状态下已经包含多少个模板串了,因为在一棵trie树上,那么定义状态也好定义,dp[len][id][s]表示长度为len时处在第id个节点并且状态为s,转移方程为dp[len+1][nextid][s|nexts] += dp[le

HDU Untitled(状压DP OR dfs枚举子集)

Untitled Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 325    Accepted Submission(s): 169 Problem Description There is an integer a and n integers b1,-,bn. After selecting some numbers from b

源哥每日一题第十四弹 hdu 1565 还是状压dp

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意:自己念 分析:有了上一题的基础,这题的状态方程也好想:dp[i][s]表示第i行第s个状态取得的最大值. 可以预处理出每一行每一种状态所能获得的值,方程就是dp[i][j] = max(dp[i][j],dp[i-1][k]+sum[i][j]); 要注意的就是判断相邻的情况:两个状态,如果a&b!=0则表示它们有相重叠的区域. #include <bits/stdc++.h> u