洛谷P1273 有线电视网

树形DP

题目传送门

同选课很像,即树上的背包问题。
f[i][j] f [ i ] [ j ] 表示以 i i 为根的树中选j个节点所能达到的最大权值。
转移方程很好想:

f[x][j]=max(f[x][j],f[ed[i].to][p]+f[x][j-p]-ed[i].dis);

因为这道题有边权,因此更新时要减掉。

但是这道题数据范围比较大,在枚举 j j p时不能直接从n开始枚举。 j j 从其目前遍历到的所有节点个数开始枚举,p从当前的节点个数开始枚举,可以并不会证明这样做复杂度为 O(n2) O ( n 2 )

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 3000
using namespace std;
struct edge{
    int next,to,dis;
};
int n,m,k;
int h[MAXN+5],f[MAXN+5][MAXN+5],w[MAXN+5];
edge ed[MAXN*2+5];
int dp(int x){
    if (x>n-m){//只有最后的几个点才有点权
        f[x][1]=w[x];
        return 1;//当前遍历到1个
    }
    int sum=0;//记录目前总共遍历了几个
    for (int i=h[x];i;i=ed[i].next){
        int t=dp(ed[i].to); sum+=t;
        for (int j=sum;j>=1;j--)
            for (int p=t;p>=1;p--)
                f[x][j]=max(f[x][j],f[ed[i].to][p]+f[x][j-p]-ed[i].dis);
    }
    return sum;
}
void addedge(int x,int y,int z){
    ed[++k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n-m;i++){
        int p,v,t;
        scanf("%d",&p);
        for (int j=1;j<=p;j++){
            scanf("%d%d",&t,&v);
            addedge(i,t,v);//这里建单向边即可
        }
    }
    for (int i=n-m+1;i<=n;i++){
        int v;
        scanf("%d",&v);
        w[i]=v;
    }
    int ans=0;
    memset(f,-63,sizeof(f));
    for (int i=1;i<=n;i++)
        f[i][0]=0;
    dp(1);
    for (int i=n;i>=1;i--)
        if (f[1][i]>=0){
            printf("%d\n",i);
            break;
        }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值