树型DP,树上的背包

来源:互联网 发布:php base64解码图片 编辑:程序博客网 时间:2024/06/02 00:26

遇到一个题目:
金矿问题
★问题描述
有一个国家,所有的国民都非常老实憨厚,某天他们在自己的国家发现了 M 座金
矿,国王知道这个消息后非常高兴,他希望能够把这些金子都挖出来造福国民。后他找
到了这个领域的专家小八,对每一座金矿进行勘测,勘测发现挖掘每个金矿所需的人数
是固定的(一个也不需要多,一个也不能少),而且这些金矿也有主矿和副矿的区别,
主矿可以直接进行挖掘,但是副矿必须在挖掘完其对应的主矿后才能进行挖掘,否则会
有相当的风险。每个主矿可能有 0 个、1 个或 2 个副矿,每个副矿都只对应 1 个主矿,
且副矿不会再有副矿。了解完这些后,国王决定派 N 个矿工去挖金矿。(N 个矿工不一定要全部用完)
★编程任务
给出金矿数量 M 及矿工数量 N ,以及每个金矿的具体情况。求最多能获得的金子数量。
★数据输入
第 1 行为两个正整数,用一个空格隔开:
NM
(其中 N(2<=N<=100),表示所有矿工数量,M(2<=M<=1000),为金矿的数量。)
从第 2 行到第 M+1 行,第 j 行给出了编号为 j-1 的金矿的基本数据,每行有 3 个
非负整数 v p q(其中 v 表示挖掘该金矿所需的矿工数目,p 表示该金矿的含金量,q 表示该金矿是主矿还是副矿。如果 q=0,表示该金矿是主矿,如果 q>0,表示该金矿为副矿,q
是所属主矿的编号)
★数据输出输出只有一行,表示最多的金子数量。
输入文件示例 输出文件示例
100 5
80 100 0
40 500 1
20 300 1
20 100 0
50 200 0 400

虽然最开始能想到是dp,但是以为状态之间存在依赖关系,所以真的无从下手被告知是树型dp,所以去看了下树上背包入门问题 hdu 1561

Problem Description
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?

Input
每个测试实例首先包括2个整数,N,M.(1 <= M <= N <= 200);在接下来的N行里,每行包括2个整数,a,b. 在第 i 行,a 代表要攻克第 i 个城堡必须先攻克第 a 个城堡,如果 a = 0 则代表可以直接攻克第 i 个城堡。b 代表第 i 个城堡的宝物数量, b >= 0。当N = 0, M = 0输入结束。

Output
对于每个测试实例,输出一个整数,代表ACboy攻克M个城堡所获得的最多宝物的数量。

Sample Input
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0

Sample Output
5
13

这个题我们大致能抽象出这样的状态:
设状态 dp[i][k][j]:第i个节点,前k个子节点,该节点和它的子节点总共用了j次攻破所获得的最大收益
状态转移: dp[i][k][j]=max(dp[i][k-1][j],dp[i][k-1][p]+dp[son(i)][k-1][j-p]
这个状态的意义是:
dp[i][k-1][j]:第k个子节点不去攻破
dp[i][k-1][p]+dp[son(i)][k-1][j-p]:去攻破第k个子节点,但子节点也有孩子,所以分配的攻破次数要枚举

/* ***********************************************Author        :TisuamaCreated Time  :2017年10月31日 星期二 16时49分32秒File Name     :挖金矿.cpp************************************************ */#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <cmath>#include <cstdlib>#include <vector>#include <queue>#include <set>#include <map>using namespace std;#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLEAR( a , x ) memset ( a , x , sizeof a )  const int INF=0x3f3f3f3f;typedef long long LL; const int maxn=1e5+100;int dp[220][220];vector<int> v[220];int val[220];int vis[220];int n,m;void dfs(int f){    vis[f]=1;    for(int i=0;i<v[f].size();i++)    {        int to=v[f][i];        if(!vis[to]) dfs(to);        for(int j=m;j>=1;j--)        {            for(int k=1;k<=j;k++)            {                dp[f][j]=max(dp[f][j],dp[f][k]+dp[to][j-k]);            }        }    }    return ;}int main(){    int a,b;    while(~scanf("%d%d",&n,&m))    {        if(n==0&&m==0)            break;        m++;        for(int i=0;i<=n;i++) v[i].clear();        CLEAR(dp,0);        CLEAR(vis,0);        for(int i=1;i<=n;i++)        {            scanf("%d%d",&a,&b);            v[a].push_back(i);            dp[i][1]=b;        }        dfs(0);        printf("%d\n",dp[0][m]);    }}

那么回到这个题,这需要稍稍变一下,上面是分配攻破次数,我们这里分配人数

/* ***********************************************Author        :TisuamaCreated Time  :2017年10月31日 星期二 21时16分19秒File Name     :挖金矿.cpp************************************************ */#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <string>#include <cmath>#include <cstdlib>#include <vector>#include <queue>#include <set>#include <map>using namespace std;#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLEAR( a , x ) memset ( a , x , sizeof a )  const int INF=0x3f3f3f3f;typedef long long LL; const int maxn=1e5+100;int dp[1100][1100];int n,m;vector<int> v[1100];int vis[1100],val[1100];void dfs(int x){    vis[x]=1;    for(int i=0;i<v[x].size();i++)    {        int to=v[x][i];        if(!vis[to]) dfs(to);        for(int j=n;j>=0;j--)        {            for(int k=val[x];k<=j;k++)                dp[x][j]=max(dp[x][j],dp[x][k]+dp[to][j-k]);        }    }    return ;}int main(){        int x,y,z;    while(~scanf("%d%d",&n,&m))    {        for(int i=0;i<=n;i++) v[i].clear();        CLEAR(dp,0);        CLEAR(vis,0);        for(int i=1;i<=m;i++)        {            scanf("%d%d%d",&x,&y,&z);            v[z].push_back(i);            dp[i][x]=y;            val[i]=x;        }        dfs(0);        printf("%d\n",dp[0][n]);            }}
原创粉丝点击