1. 程式人生 > >【Usaco2006Mar】Milk Team Select產奶比賽

【Usaco2006Mar】Milk Team Select產奶比賽

進行 狀態 ++i 分配 set iostream 代碼 沒有 stdin

【思路分析】

比賽的時候想到了用我確實也想到了樹形DP,但是狀態沒有確定對,連樣例都沒有過
PS:這是第二道發現還可以用狀態作為答案最後輸出的題目

正解:樹形DP(背包)

按照讀進來的數據,我們先建一棵樹
像這樣(這裏用vector存圖)

  for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//從父節點建一條邊連向子節點
    }

然後就是DP的過程
(本人見到的樹形DP題目比較少,但是做到過相關的題目似乎都是想這樣子的)

void dfs(int x){
    ***********************//這裏寫初始化,或者邊界判斷
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//先處理它的子樹
        ************************//這裏寫轉移方程
    }
    ***************************//這裏進行後續操作
}

確定了是樹形dp之後,我們來想一下轉移方程

f[i][j][1]表示i節點分配j個親緣關系取後最多的產奶量

f[i][j][0]表示該節點分配j個親緣關系不取後最多的產奶量

然後像背包一樣

我們給第i個子樹分配j個關系

可以得出方程

方程:

(未加初始化與後續處理)
f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));

AC代碼大概是這樣的:

代碼:

#include<cstdio>
#include<cmath>
#include<cctype>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
inline int read()
{
    char chr=getchar();
    int f=1,ans=0;
    while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
    while(isdigit(chr))  {ans=(ans<<1)+(ans<<3)+chr-'0';chr=getchar();}
    return ans*f;
}

inline void kai()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
}
int n,m,a[505];
vector<int> v[505];
int f[505][505][2];
void dfs(int x){
    for(int i=1;i<=n;i++) f[x][i][0]=f[x][i][1]=-0x3f3f3f3f;//預處理
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//遍歷子樹
        for(int j=n;j>=0;j--)
            for(int k=0;k<=j;k++)
            f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
            f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));//方程
    }
    for(int i=0;i<=n;i++) f[x][i][1]+=a[x];//後續處理
}
int ans=0,maxn=0;
int main(){
    n=read();
    m=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//建樹
    }
    dfs(0);//建一個“超級節點”(所有沒有祖先的根節點都連到了0上)
    int i;
    for(i=n;i;i--) if(f[0][i][0]>=m) break;//用狀態來做答案,找到第一個產奶量最多的親緣關系(倒續找,所以最先找到的就是最大的)
    cout<<i;
    return 0;
}

【Usaco2006Mar】Milk Team Select產奶比賽