树上背包
在这个树中选取一定数量的点或边(也可能是其他属性),使得某种与点权或者边权相关的花费最大或者最小。解决这类问题,一般要考虑使用树上背包。
dp[i][j]表示在以i为根的树中选择j个结点所获得的最大值/最小值
有线电视网
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 3005;
int n,m,cost[maxn];
int head[maxn],cnt;
int dp[maxn][maxn];
struct Edge{
int to,len,next;
}edge[maxn];
void add(int u,int v,int w)
{
cnt ++;
edge[cnt].to = v;
edge[cnt].len = w;
edge[cnt].next = head[u];
head[u] = cnt;
return ;
}
int dfs(int u)//dfs返回的是以u为根节点的树,所包含的叶子结点数
{
if(u > n-m) return 1;//说明到了叶子结点
int num = 0;
for(int i=head[u];i;i=edge[i].next)
{
int v = edge[i].to;
int nv = dfs(v);
num += nv;
for(int j=num;j>=0;j--)//类似滚动数组的思想,j要倒着写,这样j是不断变小的,如果j先更新小的,那么大的会被更新过的dp[u][j-k]更新,这是错误的
for(int k=0;k<=min(nv,j);k++)
dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]-edge[i].len);
}
return num;
}
int main()
{
memset(dp,128,sizeof(dp));//求最大值故初始化为最小
scanf("%d%d",&n,&m);
for(int i=1;i<=n-m;i++)
{
int num;
scanf("%d",&num);
for(int j=1;j<=num;j++)
{
int a,c;
scanf("%d%d",&a,&c);
add(i,a,c);
}
}
for(int i=n-m+1;i<=n;i++)
{
scanf("%d",&cost[i]);
dp[i][1] = cost[i];//叶子节点选择自己的值
}
for(int i=1;i<=n;i++) dp[i][0] = 0;//所有节点在不选择任何节点的情况下都是0
dfs(1);
for(int i=m;i>=0;i--)
{
if(dp[1][i] >= 0)
{
printf("%d",i);
break;
}
}
return 0;
}
标签:cnt,背包,浅谈,int,head,edge,maxn,树上,dp
From: https://www.cnblogs.com/-Wind-/p/18104244