通向自由的钥匙 Time Limit:10000MS Memory Limit:65536K Description 通向自由的钥匙被放n个房间里,这n个房间由n-1条走廊连接。但是每个房间里都有特别的保护魔法,在它的作用下,我无法通过这个房间,也无法取得其中的钥匙。虽然我可以通过消耗能量来破坏房间里的魔法,但是我的能量是有限的。那么,如果我最先站在1号房间(1号房间的保护魔法依然是有效的,也就是,如果不耗费能量,我无法通过1号房间,也无法取得房间中的钥匙),如果我拥有的能量为P,我最多能取得多少钥匙? Input 第一行包含两个非负整数,第一个为N,第二个为P。 Output 一行一个整数,表示取得钥匙的最大值。 Sample Input
5 5
1 2
1 1
1 1
2 3
3 4
1 2
1 3
2 4
2 5 Sample Output
7 Hint 对于20%的测试数据,有n<=20 Source |
根据题目特征我们就可以判断这是一道树形动规
那么我们究竟要不要将其转化为二叉树呢?
仔细分析:若在i节点上有j点能量,那么在j>=cost[i]的情况下,我们可以选择i,也可不选择i
如果没有选择i,那么i的所有儿子都是选不到的
如果选了i,那么i的儿子可以选,也可以不选
那么这个时候就涉及到了对多个儿子的资源分配问题,由于多叉树的资源分配的讨论比较复杂,因此我们可以用二叉树来简化状态
我们用f[x][y]来表示在x节点剩余y能量所能拿到的最多钥匙
方程: f[x][y]=max{
f[left[x]][k]+f[right[x]][y-k-cost[x]]+key[x]
f[right[x]][y] ///不选根节点x
}
这个时候在分析一下样例,我们便能发现在多叉转二叉的时候又有了一定难度,由于父子关系不明确,转化出来的二叉树可能有问题
所以我们在多叉转二叉的时候用DFS进行转化,至此,这道题就可以完美解决了
#include<iostream>
#include<cstdio>
using namespace std;
int n,p,f[105][105],r[105],l[105];
int key[105],COST[105];
bool mark[105],s[105][105];
void dfs(int x){//多叉转二叉
mark[x]=1;
for(int i=1;i<=n;i++)
if(!mark[i]&&s[x][i]){
r[i]=l[x];
l[x]=i;
dfs(i);
}
}
void dp(int x){
if(l[x])dp(l[x]);
if(r[x])dp(r[x]);
for(int y=0;y<=p;y++){
f[x][y]=f[r[x]][y];
for(int k=0;k<=y-COST[x];k++)
f[x][y]=max(f[x][y],key[x]+f[l[x]][k]+f[r[x]][y-k-COST[x]]);
}
}
int main(){
scanf("%d%d",&n,&p);
int i,x,y;
for(i=1;i<=n;i++)
scanf("%d%d",&COST[i],&key[i]);
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
s[x][y]=s[y][x]=true;
}
dfs(1);
dp(1);
cout<<f[1][p];
}