poj 1155 TELE(树形泛化背包dp)

来源:互联网 发布:软件版本更新 编辑:程序博客网 时间:2024/06/06 00:21
/*
    这道题还不错,自己想出了思路过得也比较快,也得出了一个小经验,以后写这种题先把关键部分伪代码写出来这样会快很多而且
    不那么容易出错,省去很多的调试时间
    这道题就是转化为一道树形背包问题。首先把需要付的钱转为负数,对每个叶子结点增加一个子节点表示赚的钱,为正数.
    然后记录下当前结点的所有可能的用户数目所花费的钱.所以问题就转化为一道简单的树形dp问题。最后找出盈利为非负数的最大
    用户数即可.
    有一点要注意dp数组初始化为一个很大的负数,刚开始因为这个问题wa了一遍
    用到了num数组存放每个结点的叶子结点的数目,起到很好的优化作用。
*/
#include<cstdio>#include<cstring>#include<cstdlib>#include<vector>#include<iostream>using namespace std;#define maxn 6005int dp[maxn][maxn],num[maxn],val[maxn];vector<int>sons[maxn];//num对于优化起了很大的作用,来记录当前结点子树中的叶子结点数目void dfs(int rt){    dp[rt][0]=0;    for(int u=0;u<sons[rt].size();u++){        int s=sons[rt][u];        dfs(s);        num[rt]+=num[s];        for(int i=num[rt]-num[s]+1;i<=num[rt];i++) dp[rt][i]=-1000000;  //要初始化为一个很大的负数        for(int i=num[rt];i>=1;i--){            for(int j=1;j<=num[s]&&j<=i;j++){                dp[rt][i]=max(dp[rt][i],dp[s][j]+dp[rt][i-j]+val[s]);            }        }    }}void init(){    for(int i=0;i<maxn;i++) sons[i].clear();    memset(val,0,sizeof(val));    memset(num,0,sizeof(num));}void print(){    for(int i=num[1];i>=0;i--)        if(dp[1][i]>=0)        {printf("%d\n",i);break;}}int main(){    int i,j,n,m,k,a,b;    while(~scanf("%d%d",&n,&m)){        init();        for(i=1;i<=n-m;i++){            scanf("%d",&k);            for(j=1;j<=k;j++){                scanf("%d%d",&a,&b);                sons[i].push_back(a);                val[a]=-b;            }        }        for(i=n+1;i<=n+m;i++){      //给每一个叶子结点加一个结点,用它来表示赚的钱,而其它点都表示需要支付的钱            scanf("%d",&val[i]);            num[i]=1;            sons[i-m].push_back(i);        }        dfs(1);        print();    }    return 0;}


0 1