题目链接:点这里
题解:这题算是比较裸的一道题,我们可以很明显的看出来题目是一棵树,所以我们可以先建一颗树,然后从下往上进行转移,这时候就要用到树形dp了,我们可以看到题目是会有好几颗树的,也就是森林,所以我们可以建一个0点,把这个森林连成一棵树,这时0是必选的(可以好好思考下这个点),所以这时我们是要选m+1节课,这时我们可以从下往上进行转移,这时问题有来了,假如a是b的父亲,b也有好几个孩子,那么选b的那几个孩子是最好的那(注意必须要选b),这个问题是不是很像背包?没错,这时候我们只需要在这里进行一个背包,问题就解决了,我们可以接着向上转移。
#include<bits/stdc++.h>
using namespace std;
int n, m;
int head[1005];
int cnt = 1;
int z[305];
int f[305];
vector<int> q[305];
int dp[305][305];//第i个点选了几节课
void dfs(int a)
{
for (int i = 0; i < q[a].size();i++)
{
int b = q[a][i];
dfs(b);
for (int k = m + 1; k >= 1;k--)//背包,注意k是>=1的,也就是dp[a][1]是不会更新的。
{
for (int j = 0;j<k;j++)
{
dp[a][k] = max(dp[a][k], dp[a][k - j] + dp[b][j]);
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
int a;
cin >> a >> z[i];
q[a].push_back(i);
dp[i][1] = z[i];//只选自己
}
dfs(0);
cout << dp[0][m + 1];
}
题目链接:点这里
题解:这个题和上面的题基本一样,如果你理解了上面这个题那么这个题是很简单的,唯一的不同在于dfs是返回值的,当然上面的那个题也是可以这么写的,反而可以剪掉一些不存在的情况,具体我就不多讲了,看代码。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m;
struct node
{
int to, w, next;
} e[maxn];
int head[maxn];
int f[maxn];
int dp[3005][3005];//第i个点给几个人看
int z[3005];
int cnt = 1;
void add(int u,int v,int w)
{
e[cnt] = {v, w, head[u]};
head[u] = cnt++;
}
int dfs(int a)
{
if(a>n-m)
{
dp[a][1] = z[a];
return 1;
}
int sum = 0;
for (int i = head[a]; i;i=e[i].next)
{
int to = e[i].to;
int w = e[i].w;
int b = dfs(to);
sum += b;
for (int i = m; i >= 1;i--)//你是不是觉得上面那个题的dfs可以改进一下?
{
for (int j = 1; j <= b;j++)
{
if(i>=j)
dp[a][i] = max(dp[a][i], dp[a][i - j] + dp[to][j] - w);
}
}
}
return sum;
}
int main()
{
memset(dp,~0x3f,sizeof(dp));
cin >> n >> m;
for (int i = 1; i <= n-m;i++)
{
int a;
cin >> a;
for (int j = 1; j <= a;j++)
{
int v, w;
cin >> v >> w;
f[v] = 1;
add(i, v, w);
}
}
for (int i = n-m+1; i <= n;i++)
{
cin >> z[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)
{
cout << i;
return 0;
}
}
}