题意:从一个发射站发射电视,只有叶子节点是用户,收到一部分费用,所有的边都有花费,求在不亏本的情况下,最多可以让多少用户(叶子结点)收看到电视。
分析:树形背包。
状态定义: dp(i,j) : 以 i 为根的,让 j 个用户看到电视,最大获益(可以为负数)。那么sz不再是原来的定义了。
最后遍历 j,第一个不为负数的就是答案。
状态转移:树形背包,dp(i,j) = max(d(i,j) , dp(i)(k)+dp(son,j-k)-w);
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <vector> #include <cstring> #include <cstdio> using namespace std; const int maxn = 3500; struct Edge { int u,v,cost; }; vector<Edge> G[maxn]; int n,m; int dp[maxn][maxn],tmp[maxn]; int sz[maxn]; void dfs(int u) { for(int i=0; i<G[u].size(); i++) { int v = G[u][i].v; int cc = G[u][i].cost; dfs(v); for(int j = 0; j<=sz[u]; j++) tmp[j] = dp[u][j]; for(int j=0; j<=sz[u]; j++) { for(int k=1; k<=sz[v]; k++) { dp[u][j+k] = max(dp[u][j+k],tmp[j]+dp[v][k]-cc); } } sz[u]+=sz[v]; } } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=1; i<=n-m; i++) { int p; scanf("%d",&p); for(int j=0; j<p; j++) { int v,c; scanf("%d%d",&v,&c); G[i].push_back((Edge) { i,v,c }); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dp[i][j] = -0x3f3f3f3f; memset(sz,0,sizeof(sz)); for(int i=n-m+1; i<=n; i++) { int x; scanf("%d",&x); sz[i] = 1; dp[i][1] = x; } dfs(1); for(int i = m; i>=0; i--) { if(dp[1][i]>=0) { printf("%d ",i); break; } } return 0; }