水一發水一發
題目
題面
通向自由的鑰匙被放 n 個房間裏,這 n 個房間由 n-1 條走廊連接。但是每個房間裏都有
特別的保護魔法,在它的作用下,我無法通過這個房間,也無法取得其中的鑰匙。雖然我可
以通過消耗能量來破壞房間裏的魔法,但是我的能量是有限的。那麼,如果我最先站在 1
號房間(1 號房間的保護魔法依然是有效的,也就是,如果不耗費能量,我無法通過 1 號房
間,也無法取得房間中的鑰匙),如果我擁有的能量爲 P,我最多能取得多少鑰匙?
輸入數據
第一行包含兩個非負整數,第一個爲 N,第二個爲 P。
接下來 n 行,按 1~n 的順序描述了每個房間。第 i+1 行包含兩個非負整數 cost 和 keys,
分別爲第 i 件房取消魔法需要耗費的能量和房間內鑰匙的數量。
接下來 n-1 行,每行兩個非負整數 x,y,表示 x 號房間和 y 號是連通的。
輸出數據
一行一個整數,表示取得鑰匙的最大值。
樣例
輸入:key.in
5 5
1 2
1 1
1 1
2 3
3 4
1 2
1 3
2 4
2 5
輸出: key.out
7
數據範圍
對於 20%的測試數據,有 n<=20
對於 30%的測試數據,有 n<=30
對於所有測試數據,有 p,n<=100, cost <= Maxint, keys<= Maxint
題解
題意
n個點(n-1)條邊
顯然建樹。
題目可看作簡單的樹上揹包,多叉轉二叉後
有p塊錢,分配給兒子和兄弟錢數,求最大獲利即可
需要注意的
只是需要稍微提一下到建樹方式 我纔不會說我主要調試這裏去了
又沒說給出的x,y中前面的就是父親,後面就是兒子
需要先記錄點與點之間的聯通,之後掃一遍,掃到的兒子遞歸處理即可
代碼
就是樹形dp模板題,調了一下午
老年OI選手要退役了
#include <cstdio>
#include <cstring>
const int maxn = 110;
int cost[maxn], keys[maxn];
int son[maxn], bro[maxn];
int dp[maxn][maxn];
bool vis[maxn];
int n;
inline void Swap(int &a, int &b){int t = a; a = b, b = t;}
void Line(int rt){
int i = 1, fg = 0;
while (!dp[rt][i] && i <= n) ++i;
if (i > n) return ;
dp[i][rt] = 0, son[rt] = i;
Line(i); int ls = i;
for (++i; i <= n; ++i){
if (dp[rt][i]){
dp[i][rt] = 0, Line(i);
bro[ls] = i, ls = i;
}
}
}
int f(int rt, int pw){
if (!rt || pw < 0) return 0;
if (dp[rt][pw] != -1) return dp[rt][pw];
int res = f(bro[rt], pw);
int res1;
for (int i = cost[rt]; i <= pw; ++i){
res1 = f(son[rt], i - cost[rt]) + f(bro[rt], pw - i) + keys[rt];
if (res1 > res) res = res1;
}
return dp[rt][pw] = res;
}
int main (){
freopen ("key.in", "r", stdin);
freopen ("key.out", "w", stdout);
int p; scanf ("%d%d", &n, &p);
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; ++i) scanf ("%d%d", cost + i, keys + i);
memset(vis, 0, sizeof(vis));
for (int i = 1, x, y; i < n; ++i){
scanf ("%d%d", &x, &y);
dp[x][y] = dp[y][x] = 1;
}
Line(1);
memset(dp, -1, sizeof(dp));
printf ("%d", f(1, p));
return 0;
}