题目链接:https://www.luogu.com.cn/problem/P1273
树形dp。但这个本质上是个背包问题,一时间没有想起来有依赖的背包问题。
读题的时候没有发现客户端一定是接在转发器上的,想成了一颗比较自由的树。
做的时候要当作一个有依赖的背包问题来做,分好组别,找出每个fij的最优值,直接倒序找>=0的那个下标
code:
#include<bits/stdc++.h> using namespace std; #pragma GCC optimize(2) #define ll long long inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } const int nn = 3010; struct tdata{ int num; int cost; }; vector<tdata> ve[nn]; int n,m; int w[nn]; int f[nn][nn]; //本质上是一个有依赖的背包问题,所求的是数量 //f[i][j] 表示i个节点选择j个用户所获得的的最大的价值,我们只需要保持这个价值不小于0就可以 //需要注意的地方是,客户端只能接在转播站上==这是读题的时候没有注意到的 //那么答案就是倒序找个>=0的值就可以了 int dfs(int u){ if(u>n-m){ f[u][1] = w[u]; return 1; } int sum = 0,t = 0; for(int i=0;i<ve[u].size();i++){ int v = ve[u][i].num; int c = ve[u][i].cost; t = dfs(v);sum+=t; //分组背包的倒序 for(int j = sum;j>0;j--){ for(int k=1;k<=t;k++) if(j-k>=0) f[u][j] = max(f[u][j],f[u][j-k]+f[v][k]-c); } } return sum; } int main() { //cin.ignore(numeric_limits<streamsize>::max(),'\n') cin>>n>>m; int k; memset(f,~0x3f,sizeof f); for(int i=1;i<=n-m;i++){ cin>>k; int a,b; for(int j=1;j<=k;j++){ cin>>a>>b; ve[i].push_back({a,b}); } } for(int i=n-m+1;i<=n;i++){ cin>>w[i]; } for(int i=1;i<=n;i++) f[i][0] = 0; dfs(1); for(int i=m;i>0;i--){ if(f[1][i]>=0){ cout<<i<<endl; break; } } }