作者:小白菜 | 来源:互联网 | 2023-08-18 13:44
树形DP——P1273有线电视网这道题和之前的那个一样是分组背包。我发现分组背包的套路都是这样省的:一般这个题目的dp[i][j ]的意思是以i这个节点为根的子树,容纳j个***东
树形DP——P1273 有线电视网
这道题和之前的那个一样是分组背包。我发现分组背包的套路都是这样省的:
一般这个题目的dp[ i ][ j ]的意思是以 i 这个节点为根的子树,容纳 j 个***东西所获得的最大价值。
上面的temp是u这个节点的下面的最大子树返回的 那个***的东西的数量(就是 j 那个位置代表的东西), 然后 sum 这个值是以 u 这个节点为根节点的子树返回的的那个***的值。然后基本的思路就是和分组背包一样,把每个子树看成一组,然后通过子树的组来更新大的组。
然后注意这里的边权,如果要加入以 j 为根节点的子树的***的东西,那么必须要花费从 u -> v 这个的边权的花费。
至于初始化的话基本就是初始化叶子节点的值。
code
#include
#include<string>
#include
using namespace std;
const int MAXN = 3e3+10;
int cost[MAXN];
int dp[MAXN][MAXN];
struct Edge{
int v, next, w;
}edge[MAXN];
int head[MAXN];
int cnt;
void addEdge(int u, int v, int w){
edge[cnt].v = v;
edge[cnt].next = head[u];
edge[cnt].w = w;
head[u] = cnt++;
return;
}
void ini(int n, int m)
{
for(int i = 1; i <= n; i++)
head[i] = -1;
cnt = 0;
// for(int i = 1; i <= n; i++)
// {
// for(int z = 1; z <= m; z++)
// dp[i][z] = -1;
// }
memset(dp, ~0x3f, sizeof(dp));
for(int i = 1; i <= n; i++)
dp[i][0] = 0;
return;
}
int dfs(int u)
{
int sum;
if(cost[u])
sum = 1;
else
sum = 0;
int temp;
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].v;
temp = dfs(v); sum +=temp;
for(int z = sum; z >= 1; z--)
{
for(int j = 1; j <= z&& j <= temp; j++)
{
dp[u][z] = max(dp[u][z], dp[u][z-j]+dp[v][j]-edge[i].w);
}
}
}
return sum;
}
int main()
{
int n, m;
cin >> n >> m;
ini(n, m);
int num1 = n-m;
for(int i = 1; i <= num1; i++)
{
int num2, v, w;
cin >> num2;
for(int z = 1; z <= num2; z++)
{
cin >> v >> w;
addEdge(i, v, w);
}
}
for(int i = n-m+1; i <= n; i++)
{
cin >> cost[i];
dp[i][1] = cost[i];
}
dfs(1);
int ans = 0;
for(int i = 1; i <= m; i++)
{
if(dp[1][i] >= 0) ans = i;
}
cout << ans;
return 0;
}