poj1155(树形dp+背包)

src:http://poj.org/problem?id=1155

思路:dp[u][i]指子树u取i个叶子的最优值,Max(dp[u][i],dp[u][i-k]+dp[v][k]-edge[i].v),注意是从前几个阶段转移后的最优值中选(例codeforces219D(树形dp) ,dp[u][i]指节点依赖于i子节点),还是前几个阶段转移过来的共同构成(如此题,dp[u][i]表示结点u取i个前面的阶段)。

此题思路是首先知道回到根节点dp【u】【i】最多挑选几个叶子节点, 那我们就可以确定子问题就是dp【v】【】是在子节点v是最多挑选几个叶节点

ac代码:

#include <iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<functional>
#include<utility>
#include<string>
#include<string.h>
#include<vector>
#include<iomanip>
#include<stack>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
const int inf=99999999;

int n,m,dp[3001][3001],head[3001],Enum=0,val[3001],sz[3001];
struct node
{
    int to,v,ne;
}edge[3005];

void add_edge(int a,int b,int v){edge[Enum].to=b;edge[Enum].v=v;edge[Enum].ne=head[a];head[a]=Enum++;}
void init()
{
    memset(head,-1,sizeof(head));
    memset(sz,0,sizeof(sz));
}
void dfs1(int u)//确定各节点的背包大小(可选的叶子节点数量)
{
    if(u>n-m){sz[u]=1;return;}
    for(int i=head[u];i!=-1;i=edge[i].ne){
        int v=edge[i].to;
        dfs1(v);
        sz[u]+=sz[v];
    }
    FOR(i,1,sz[u])dp[u][i]=-inf;
}
void dfs2(int u)
{
    if(u>n-m){
        dp[u][1]=val[u];return;
    }
    for(int i=head[u];i!=-1;i=edge[i].ne){
        int v=edge[i].to;
        dfs2(v);
        for(int j=sz[u];j>=1;j--)for(int k=1;k<=sz[v];k++)if(j>=k){
            Max(dp[u][j],dp[u][j-k]+dp[v][k]-edge[i].v);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m){
        init();
        int tt,b,v;
        for(int i=1;i<=n-m;i++){
            cin>>tt;
            for(int j=1;j<=tt;j++){
                cin>>b>>v;
                add_edge(i,b,v);
            }
        }
        dfs1(1);
        FOR(i,n-m+1,n)cin>>val[i];
        dfs2(1);
        int ans=0;
        for(int i=sz[1];i>=1;i--)if(dp[1][i]>=0){ans=i;break;}
        cout<<ans<<endl;
    }
    return 0;
}

 

posted @ 2018-05-15 20:54  WindFreedom  阅读(247)  评论(0)    收藏  举报