zoukankan      html  css  js  c++  java
  • 【洛谷1273】有线电视网(树上背包)

    点此看题面

    大致题意: 给你一棵带权树,已知每连接一条边需要一定花费,如果某个叶节点能到达根,可以获得一定收益。问在不亏本的情况下,最多能使多少个叶节点能到达根。

    树上背包

    这是一道比较经典的树上背包题。

    如何记录状态

    我们可以用(f_{i,j})表示在以(i)为根的子树内选择(j)个叶节点能得到的最大收益,并用(Size_i)表示(i)为根的子树内的叶节点数目

    那么答案就是找到一个最大的(ans)使得(f_{1,ans}≥0)

    如何转移

    树上背包的转移与普通背包是十分相似的:

    [f_{x,j}=max(f_{x,j},f_{x,j-k}+f_{son,k}-val) ]

    其中(val)表示这条边的边权。

    注意(j)(k)的枚举范围,其中(1≤j≤Size_x+Size_{son},1≤k≤Size_{son})

    虽然复杂度看似(O(n^3))的,但实际上是达不到上界的,于是虽然(1≤n≤3000),依然能轻松跑过。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1)) 
    #define N 3000
    #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=z)
    using namespace std;
    int n,m,ee=0,lnk[N+5],val[N+5];
    struct edge
    {
        int to,nxt,val;
    }e[N+5];
    class FIO
    {
        private:
            #define Fsize 100000
            #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
            #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
            int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
        public:
            FIO() {FinNow=FinEnd=Fin;}
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
            inline void read_char(char &x) {while(isspace(x=tc()));}
            inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
            inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
            inline void write_char(char x) {pc(x);}
            inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
            inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_TreeDP//树形DP求解树上背包
    {
        private:
            int f[N+5][N+5],Size[N+5];//f[i][j]表示在以i为根的子树内选择j个叶节点能得到的最大收益,Size[i]表示以i为根的子树内的叶节点数目
            inline void DP(int x)//DP的核心过程
            {
                register int i,j,k;
                for(i=1;i<=m;++i) f[x][i]=-INF;//初始化
                if(x>n-m) return (void)(f[x][1]=val[x],Size[x]=1);//如果当前节点是叶节点,修改f[x][1]为val[x],Size[x]为1,然后return
                for(i=lnk[x];i;i=e[i].nxt)//枚举子节点
                {
                    for(DP(e[i].to),j=Size[x]+Size[e[i].to];j;--j)//背包转移
                        for(k=1;k<=Size[e[i].to];++k) f[x][j]=max(f[x][j],f[x][j-k]+f[e[i].to][k]-e[i].val);
                    Size[x]+=Size[e[i].to];//将Size[x]加上Size[e[i].to]
                }
            }
        public:
            inline int GetAns() 
            {
                register int i;
                for(DP(1),i=m;i;--i) if(f[1][i]>=0) return i;//找到最大的i使f[1][i]>=0
                return 0;
            } 
    }TreeDP;
    int main()
    {
        register int i,t,x,y;
        for(F.read(n),F.read(m),i=1;i<=n-m;++i) for(F.read(t);t;--t) F.read(x),F.read(y),add(i,x,y);
        for(;i<=n;++i) F.read(val[i]);
        return F.write(TreeDP.GetAns()),F.end(),0;
    }
    
  • 相关阅读:
    同一个IIS绑定多个Htts 站点问题
    开发小程序心得
    在阿里云里申请免费Https证书SSL
    request:fail 小程序要求的 TLS 版本必须大于等于 1.2
    js封装Cookie操作
    数据库日志文件(databasename_log.ldf)太大 如何清除
    sql with(lock) 与事务
    java.lang.VerifyError: com/google/android/gms/measurement/internal/zzw
    Eclipse 导入 Android studio Exception Ljava/lang/UnsatisfiedLinkEror
    Eclipse工程 导入 Android Studio
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu1273.html
Copyright © 2011-2022 走看看