poj1155

时间:2023-03-09 17:50:38
poj1155

题意:给定一个树形图,节点数量3000。叶子节点是用户,每个用户如果能看上电视会交一定的电视费。看上电视的条件是从根到该用户的路径全部被修好,修每条边有一个费用。在不亏损(用户交钱总额>=修路总费用)的前提下,最多有多少人能看上电视。

分析:树形dp。dp[u][i][j]表示对于u节点,只看其前i个儿子对应的子树,在这些子树中总共让j个用户看上电视,这样的最大利润是多少(可以为负值)。

那么dp[u][i][j]=max(dp[u][i-1][j], dp[v][num[v][k] + dp[u][i - 1][j - k] - cost(u, v));其中v是u第i个儿子,num[v]表示以v为根的子树中总共有多少用户,cost(u,v)表示u到v这条边需要的花费。

以上是非叶子节点的转移方法,对于叶子节点则稍有不同,直接赋值为该用户交的钱即可。

#include <cstdio>
#include <vector>
using namespace std; #define D(x) const int INF = 0x3f3f3f3f;
const int MAX_N = (int)(3e3) + ; int n, m;
int money[MAX_N];
vector<pair<int, int> > edge[MAX_N];
int dp[MAX_N][MAX_N];
int num[MAX_N];
int sum[MAX_N]; void input()
{
scanf("%d%d", &n, &m);
for (int i = ; i <= n - m; i++)
{
int k;
scanf("%d", &k);
for (int j = ; j < k; j++)
{
int a, c;
scanf("%d%d", &a, &c);
edge[i].push_back(make_pair(a, c));
}
}
for (int i = n - m + ; i <= n; i++)
{
scanf("%d", &money[i]);
}
} void dfs(int u, int cost)
{
if (u > n - m)
{
dp[u][] = ;
num[u] = ;
sum[u] = money[u];
dp[u][] = money[u];
return;
} num[u] = ;
sum[u] = ;
for (int i = ; i < (int)edge[u].size(); i++)
{
int v = edge[u][i].first;
int w = edge[u][i].second;
dfs(v, w);
num[u] += num[v];
sum[u] += sum[v];
}
fill_n(dp[u], num[u] + , -INF);
dp[u][] = ;
for (int i = ; i < (int)edge[u].size(); i++)
{
int v = edge[u][i].first;
int w = edge[u][i].second;
for (int j = num[u]; j >= ; j--)
{
int limit = min(j, num[v]);
for (int k = ; k <= limit; k++)
{
dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - k] - w);
}
}
}
} int main()
{
input();
dfs(, );
int ans = ;
for (int i = ; i <= num[]; i++)
{
if (dp[][i] >= )
ans = max(ans, i);
}
for (int i = ; i <= n; i++)
{
for (int j = ; j <= num[i]; j++)
{
D(printf("dp[%d][%d]=%d\t", i, j, dp[i][j]));
}
D(puts(""));
}
printf("%d\n", ans);
return ;
}