interlinkage:
https://jzoj.net/senior/#contest/show/2703/0
description:
solution:
- 注意到这本质就是一个背包,只是选了一个点就必须把它到根节点的所有的点都选上
- 考虑如何转移这个背包,发现一个点要么转移到$dfs$序比它大$1$的点上,要么转移到比这个点子树中$dfs$序最大的点的$dfs$序大$1$的点上
- 前者表示这条链继续选,后者表示放弃这条链
- 易得所有的状态都会被转移到
- 这是一个很经典的问题
code:
#include<algorithm> #include<cstring> #include<iostream> #include<cstdio> using namespace std; const int N=5e3+15; const int M=1e4+15; int n,p,ans,tot,tim; int w[N],f[N],v[N],head[N],dp[N][M],id[N],st[N],ed[N]; struct EDGE { int to,nxt; }edge[N<<1]; inline int read() { char ch=getchar();int s=0,f=1; while (ch<‘0‘||ch>‘9‘) {if (ch==‘-‘) f=-1;ch=getchar();} while (ch>=‘0‘&&ch<=‘9‘) {s=(s<<3)+(s<<1)+ch-‘0‘;ch=getchar();} return s*f; } void add(int u,int v) { edge[++tot]=(EDGE){v,head[u]}; head[u]=tot; } void dfs(int x) { st[x]=++tim;id[tim]=x; for (int i=head[x];i;i=edge[i].nxt) dfs(edge[i].to); ed[x]=tim; } int main() { freopen("medicine.in","r",stdin); freopen("medicine.out","w",stdout); n=read();p=read(); w[1]=read();f[1]=read();v[1]=read(); for (int i=2;i<=n;i++) { w[i]=read();f[i]=read();v[i]=read(); add(f[i],i); } dfs(1); memset(dp,-0x3f,sizeof(dp)); dp[1][0]=0; for (int i=1;i<=tim;i++) { int x=id[i]; for (int j=0;j<=p;j++) if (dp[i][j]>=0) { if (j+w[x]<=p) dp[i+1][j+w[x]]=max(dp[i+1][j+w[x]],dp[i][j]+v[x]); dp[ed[x]+1][j]=max(dp[ed[x]+1][j],dp[i][j]); ans=max(ans,dp[i][j]); } } for (int i=0;i<=p;i++) ans=max(ans,dp[tim+1][i]); printf("%d\n",ans); return 0; }
原文地址:https://www.cnblogs.com/xxzh/p/10668919.html
时间: 2024-10-13 04:31:54