P1273 有线电视网
紧接着的一道树形dp。
这道题直接告诉你这是一颗树了。叶子节点是客户。
连边要费用,客户会付钱。求不亏本情况下能满足的最大用户。
还是老思路:用dp[i][j]
表示第i个节点的子树下,取j个客户的最大值。
还是那个方程:dp[i][j] = max(dp[i][j], dp[i][j - k] + dp[v][k] - cost)
要不亏本,这个值肯定要大于0嘛。所以逆序搞一搞就好了。
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 3005;
struct Edges
{
int next, to, weight;
} e[maxn << 2];
int head[maxn], tot;
int n, m;
int dp[maxn][maxn];
int w[maxn];
void link(int u, int v, int w)
{
e[++tot] = (Edges){head[u], v, w};
head[u] = tot;
}
int dfs(int u)
{
if(u > n - m)
{
dp[u][1] = w[u];
return 1;
}
int ans = 1;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
int t = dfs(v);
ans += t;
for(int j = ans; j >= 0; j--)
{
for(int k = 0; k <= t; k++)
{
if(j - k >= 0) dp[u][j] = std::max(dp[u][j], dp[u][j - k] + dp[v][k] - e[i].weight);
}
}
}
return ans;
}
int main()
{
for(int i = 0; i < maxn; i++) for(int j = 0; j < maxn; j++) dp[i][j] = -19260817;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n - m; i++)
{
int k; scanf("%d", &k);
while(k--)
{
int v, w; scanf("%d%d", &v, &w);
link(i, v, w);
}
}
for(int i = n - m + 1; i <= n; i++)
{
scanf("%d", &w[i]);
}
for(int i = 1; i <= n; i++) dp[i][0] = 0;
dfs(1);
for(int i = m; i >= 1; i--)
{
if(dp[1][i] >= 0)
{
printf("%d\n", i);
break;
}
}
return 0;
}