题意
1为根节点,有m个叶节点,其他为中间结点,每个结点的权值代表他愿意付出的钱,边的权值代表联通两节点的代价,求不亏损的情况下最多能到达多少个叶节点。
思路
另dp[u][i]表示根节点为u的子树此时装了i个叶节点的盈利情况(
∑moneyi−∑Edgecosti
)
那么我们就能得到这样的状态转移方程:
如果u是叶节点
dp[u][1]=1
如果u是根节点
dp[u][j]=max(dp[u][j],dp[u][j−num]+dp[v][num]−costuv
,v是u的儿子节点。
代码
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
#define lowbit(x) ((x)&(-x))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
#define MP(a, b) make_pair(a, b)
const int INF = 0x3f3f3f3f;
const int maxn = 1e4 + 7;
const double eps = 1e-8;
const int MOD = 1000000009;
const double PI = acos(-1.0);
vector<pair<int, int> > G[maxn];
int a[maxn], dp[maxn][maxn];
int n, m, tot;
int sum[maxn];
void init(int u, int fa)
{
sum[u] = 1;
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i].first;
if (v != fa)
{
init(v, u);
sum[u] += sum[v];
}
}
}
void dfs(int u, int fa, int pre)
{
dp[u][0] = 0;
if (G[u].size() == 0)
{
dp[u][1] = a[i];
return ;
}
else
{
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i].first;
int w = G[u][i].second;
if (v != fa)
{
dfs(v, u, w);
for (int j = sum[u]; j >= 1; j--)
for (int k = sum[v]; k >= 1; k--)
if (j >= k && dp[u][j] != -INF && dp[v][k] != INF)
dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k] - w);
}
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while (scanf("%d%d", &n, &m) != EOF)
{
for (int i = 1; i <= n; i++)
G[i].clear();
memset(dp, -INF, sizeof(dp));
for (int i = 1; i <= n - m; i++)
{
int u, v, k;
scanf("%d", &k);
while (k--)
{
scanf("%d%d", &u, &v);
G[i].push_back(MP(u, v));
G[u].push_back(MP(i, v));
}
}
for (int i = n - m + 1; i <= n; i++)
scanf("%d", &a[i]);
init(1, -1);
dfs(1, -1, 0);
for (int i = m; i >= 0; i--)
{
if (dp[1][i] >= 0)
{
printf("%d\n", i);
break;
}
}
}
return 0;
}