zoukankan      html  css  js  c++  java
  • P1273 有线电视网 树形dp

      

    题目描述

    某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

    从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

    现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

    写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

    输入输出格式

    输入格式:

    输入文件的第一行包含两个用空格隔开的整数N和M,其中2≤N≤3000,1≤M≤N-1,N为整个有线电视网的结点总数,M为用户终端的数量。

    第一个转播站即树的根结点编号为1,其他的转播站编号为2到N-M,用户终端编号为N-M+1到N。

    接下来的N-M行每行表示—个转播站的数据,第i+1行表示第i个转播站的数据,其格式如下:

    K A1 C1 A2 C2 … Ak Ck

    K表示该转播站下接K个结点(转播站或用户),每个结点对应一对整数A与C,A表示结点编号,C表示从当前转播站传输信号到结点A的费用。最后一行依次表示所有用户为观看比赛而准备支付的钱数。

    输出格式:

    输出文件仅一行,包含一个整数,表示上述问题所要求的最大用户数。

    输入输出样例

    输入样例#1: 复制
    5 3
    2 2 2 5 3
    2 3 2 4 3
    3 4 2
    输出样例#1: 复制
    2
    

    说明

    样例解释

    如图所示,共有五个结点。结点①为根结点,即现场直播站,②为一个中转站,③④⑤为用户端,共M个,编号从N-M+1到N,他们为观看比赛分别准备的钱数为3、4、2,从结点①可以传送信号到结点②,费用为2,也可以传送信号到结点⑤,费用为3(第二行数据所示),从结点②可以传输信号到结点③,费用为2。也可传输信号到结点④,费用为3(第三行数据所示),如果要让所有用户(③④⑤)都能看上比赛,则信号传输的总费用为:

    2+3+2+3=10,大于用户愿意支付的总费用3+4+2=9,有线电视网就亏本了,而只让③④两个用户看比赛就不亏本了。

    非常好的一道树形dp

    用了分组背包的思想dp[i][j]表示   i结点连接 j 个终端所能获取的最大利益

    背包的总容量相当于该点为根节点的子树中所有的用户数量(dp[i][j]的 j 不可能超过它连接的所有用户数)。然后,把该节点的每个儿子看成一组,每组中的元素为选一个,选两个...选n个用户。

    分组dp的伪代码:   (就是把01背包反过来  这样就保证每组物品只取一个!)

    #include<bits/stdc++.h>
    using namespace std;
    //input by bxd
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i>=(b);--i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m)
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s);
    #define ll long long
    #define pb push_back
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define inf 0x3f3f3f3f
    #define lson l,m,pos<<1
    #define rson m+1,r,pos<<1|1
    const int N=3000+5;
    int pos,head[2*N];
    struct Edge
    {
        int to,nex,v;
    }edge[N*2];
    void add(int a,int b,int c)
    {
        edge[++pos].nex=head[a];
        head[a]=pos;
        edge[pos].to=b;
        edge[pos].v=c;
    }
    int node[N];
    int dp[N][N];
    int m,n;
    
    int dfs(int u,int fa)
    {
        if(u>n-m)
        {
            dp[u][1]=node[u];
            return 1;
        }
    
        int sum=0;
        for(int i=head[u];i;i=edge[i].nex)
        {
            int v=edge[i].to;
            if(v==fa)continue;
            int t=dfs(v,u);
            sum+=t;
            repp(j,sum,1)
            rep(k,1,t)
            if(j>=k)
            dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-edge[i].v);
        }
        return sum;
    }
    int main()
    {
        RII(n,m);
        rep(i,1,n-m)
        {
            int q;RI(q);
            while(q--)
            {
                int a,b;RII(a,b);
                add(i,a,b);
            }
        }
        rep(i,n-m+1,n)
        RI(node[i]);
        CLR(dp,-0x3f);
        rep(i,1,n)dp[i][0]=0;
        dfs(1,0);
    
        repp(i,m,1)
        {
            if(dp[1][i]>=0)
            {
                cout<<i;break;
            }
        }
    }
    View Code
  • 相关阅读:
    让iis支持中文文件名(转)
    为你的mail server增加SPF记录
    sql清除事务日志命令
    收集的ASP.NET中常用正则表达式
    在线支付类封装
    提供一个操作Windows服务类库(基本函数)
    简单测试Newtonsoft.json JObject内存占用分配
    图片和文字同行 对齐方式
    常用的css(持续跟新中....)
    Effective Ways to Develop Web Part
  • 原文地址:https://www.cnblogs.com/bxd123/p/10848580.html
Copyright © 2011-2022 走看看