poj 1155 树形dp+分组背包

来源:互联网 发布:js class 写法例子 编辑:程序博客网 时间:2024/05/16 11:09

题目:点击打开链接

题意:

有一个电视台要用电视网络转播节目。这种电视网络是一树,树的节点为中转站或者用户。树节点的编号为1~N,其中1为总站,2~(N-M)为中转站,(总站和中转站统称为转发站)N-M+1~N为用户,电视节目从一个地方传到另一个地方都要费用,同时每一个用户愿意出相应的钱来付电视节目。现在的问题是,在电视台不亏本的前提下,要你求最多允许有多少个用户可以看到电视节目。

输入:

N M N表示转发站和用户总数,M为用户数

以下N-M行,第i行第一个K,表示转发站i和K个(转发站或用户)相连, 其后第j对数val,cost表示,第i个转发站到val有边,费用cost.

最后一行M个数表示每个用户愿意负的钱。

输出:

不亏本前提下,可以收到节目最多的用户数。

(如果某个用户要收到节目(叶子结点),那么电视台到该用户的路径节点的费用都要付)

思路:在树上进行背包,对于以u为根的子树,该子树供给给j个用户亏本的最少钱


#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cmath>using namespace std;const int N=3000+5;const int INF=1000000000;struct edge{    int v,w,next;}e[N];int cnt,f[N][N],head[N],num[N];int n,m;void addedge(int u,int v,int w){    e[cnt].v=v;    e[cnt].w=w;    e[cnt].next=head[u];    head[u]=cnt++;}void init(){    cnt=0;    memset(head,-1,sizeof(head));    memset(num,0,sizeof(num));    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++)            f[i][j]=-INF;    }}void dfs(int u){    if(u>=n-m+1)return;    for(int i=head[u];~i;i=e[i].next){        int v=e[i].v;        dfs(v);        num[u]+=num[v];        for(int j=num[u];j>=0;j--){            for(int k=1;k<=num[v];k++){                if(j<k)break;                f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-e[i].w);            }        }//        for(int j=num[u];j>=0;j--){  //            for(int k=1;k<=num[v];k++){//                f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-e[i].w);  //也有人这样写,其实都一样//            }//        }    }}int main(){   // freopen("f.txt","r",stdin);    scanf("%d%d",&n,&m);    int s;    init();    int u,v,w;    for(int i=1;i<=n-m;i++){        scanf("%d",&s);        while(s--){            scanf("%d%d",&v,&w);            addedge(i,v,w);        }    }    for(int i=n-m+1;i<=n;i++){        scanf("%d",&f[i][1]);        num[i]=1;    }    dfs(1);    for(int i=m;i>=0;i--){        if(f[1][i]>=0){            printf("%d\n",i);            break;        }    }    return 0;}



0 0
原创粉丝点击