1. 程式人生 > >Codeforces815C Karen And SuperMarket 解題報告【樹上DP/樹上揹包(?)】

Codeforces815C Karen And SuperMarket 解題報告【樹上DP/樹上揹包(?)】

On the way home, Karen decided to stop by the supermarket to buy some groceries.

She needs to buy a lot of goods, but since she is a student her budget is still quite limited. In fact, she can only spend up to b dollars.
The supermarket sells n goods. The i-th good can be bought for ci dollars. Of course, each good can only be bought once.
Lately, the supermarket has been trying to increase its business. Karen, being a loyal customer, was given n coupons. If Karen purchases the i-th good, she can use the i-th coupon to decrease its price by di. Of course, a coupon cannot be used without buying the corresponding good.
There is, however, a constraint with the coupons. For all i ≥ 2, in order to use the i-th coupon, Karen must also use the xi-th coupon (which may mean using even more coupons to satisfy the requirement for that coupon).
Karen wants to know the following. What is the maximum number of goods she can buy, without exceeding her budget b?
Input


The first line of input contains two integers n and b (1 ≤ n ≤ 5000, 1 ≤ b ≤ 109), the number of goods in the store and the amount of money Karen has, respectively.
The next n lines describe the items. Specifically:
The i-th line among these starts with two integers, ci and di (1 ≤ di < ci ≤ 109), the price of the i-th good and the discount when using the coupon for the i-th good, respectively.
If i ≥ 2, this is followed by another integer, xi (1 ≤ xi < i), denoting that the xi-th coupon must also be used before this coupon can be used.
Output

Output a single integer on a line by itself, the number of different goods Karen can buy, without exceeding her budget.
Examples
input
6 16
10 9
10 5 1
12 2 1
20 18 3
10 2 3
2 1 5
output
4
input
5 10
3 1
3 1 1
3 1 2
3 1 3
3 1 4
output
5
Note
In the first test case, Karen can purchase the following 4 items:
Use the first coupon to buy the first item for 10 - 9 = 1 dollar.
Use the third coupon to buy the third item for 12 - 2 = 10 dollars.
Use the fourth coupon to buy the fourth item for 20 - 18 = 2 dollars.
Buy the sixth item for 2 dollars.
The total cost of these goods is 15, which falls within her budget. Note, for example, that she cannot use the coupon on the sixth item, because then she should have also used the fifth coupon to buy the fifth item, which she did not do here.
In the second test case, Karen has enough money to use all the coupons and purchase everything.
解題報告

這道題的題意就是說有n個商品,每個的價值為ci,每個商品有自己的優惠券,可以優惠di元。除開第一個優惠券,每個優惠券的使用都依賴於前面的某個優惠券(xi)。問我們用b元最多能買多少個商品。
這道題可以說是HDU1561的衍生,都是樹上有依賴的揹包問題。而HDU1561的依賴並不是指優惠券的依賴,而是指商品與商品的依賴,也就是說對於商品v必須買了商品u才能買商品v。
他的轉移方程是這樣的:

for(int j=size[u];j>=1;j--)//這裡的size[u]不包括size[v]
for(int k=1;k<=size[v];k++)
dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]);

這裡的dp[u][j+k]表示以u為根節點的子樹在選擇了j+k個節點後的最優答案。
我們考慮怎樣處理優惠券的有關問題。
我們將上面的dp陣列加上半維,也就是
dp[u][j+k][0/1]表示以u為根基點的子樹選擇了j+k個節點,u這個點的優惠券不用/用後的最優答案。
對於初值,我們不難發現:

dp[u][0][0]=0;
dp[u][1][0]=c[u];
dp[u][1][1]=c[u]-d[u];

其餘的賦+inf。
那麼怎麼轉移呢?
顯然我們當前的dp[u][j+k][0/1]的狀態要從他的子樹們轉移過來,而他的子樹能否使用優惠券實際上由u這個點決定。
那麼無非就是u使用優惠券與否的問題。
如果u不使用優惠券,其子樹的每個節點自然也不能使用,也就是說從dp[u][j][0]+dp[v][k][0]這個答案轉移過來。
如果u使用優惠券,那麼對於v及其子樹就有兩種決斷,一種是v使用優惠券,一種是v不使用優惠券,也就是說從dp[u][j][1]+dp[v][k][1];dp[u][j][1]+dp[v][k][0]兩個答案轉移過來。
至於最後的答案,我們在dp[1][n~1][0/1]裡面找第一個小於等於b的答案的下標就行了。
(據說這道題的時間複雜度可以證明是O(N^2)的)
程式碼如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5000,inf=0x3f3f3f3f;
struct edge
{
    int v,next;
}ed[N+5];
int head[N+5],num;
int n,b,ans;
int c[N+5],d[N+5],x[N+5],w[N+5];
int dp[N+5][N+5][2],size[N+5];
void build(int u,int v)
{
    ed[++num].v=v;
    ed[num].next=head[u];
    head[u]=num;
}
void dfs(int u)
{
    size[u]=1;
    dp[u][0][0]=0;
    dp[u][1][0]=c[u];
    dp[u][1][1]=c[u]-d[u];
    for(int i=head[u];i!=-1;i=ed[i].next)
    {
        int v=ed[i].v;
        dfs(v);
        for(int j=size[u];j>=0;j--)
        for(int k=0;k<=size[v];k++)//合併v的子樹的答案和u已經便利到的子樹中除開v的所有子樹的答案 
        dp[u][j+k][0]=min(dp[u][j+k][0],dp[u][j][0]+dp[v][k][0]),
        dp[u][j+k][1]=min(dp[u][j+k][1],dp[u][j][1]+dp[v][k][1]),
        dp[u][j+k][1]=min(dp[u][j+k][1],dp[u][j][1]+dp[v][k][0]);
        size[u]+=size[v];
    }
}
int main()
{
    while(~scanf("%d%d",&n,&b))
    {
        memset(head,-1,sizeof(head));num=0;
        memset(dp,inf,sizeof(dp));
        scanf("%d%d",&c[1],&d[1]);
        for(int i=2;i<=n;i++)
        {
            int u;
            scanf("%d%d%d",&c[i],&d[i],&u);
            build(u,i); 
        }
        dfs(1);
        ans=n;
        while(dp[1][ans][0]>b&&dp[1][ans][1]>b)ans--;
        printf("%d\n",ans);
    }
    return 0;
}