洛谷p2014选课
此题的重点在于两个部分:一是如何将森林转换成一棵树,原则是添加根为0的点,将各个树链接起来;二是循环的顺序问题,因为每一种课只能选择一遍,所以只能倒序循环,有点儿想01背包
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=305; const int maxm=305; int n,m,cnt,a[maxn],head[maxn],dp[maxn][maxm],tot[maxn]; //f[i][k] = max(f[i][k], f[i][k - p] + f[x][p]) (1 ≤ k ≤ m , 0 ≤ p < k) //p < k 的原因是当 p = k 的时候 f[i][k - p] 中 k - p 的值等于 0, //这说明 i 这个节点无法被选择到,由于 i 是 x 节点的先修课,不选择 i 节点便无法选择 x 节点, //所以应满足 p < k struct node{ int to,nxt; }ed[maxn*2]; void addedge(int f,int t) { ed[++cnt].to=t; ed[cnt].nxt=head[f]; head[f]=cnt; } void dfs(int u) { dp[u][1]=a[u]; tot[u]=1; for(int i=head[u];i!=0;i=ed[i].nxt) { int v=ed[i].to; dfs(v); tot[u]+=tot[v];//这是优化的部分 //这里为什么要倒序 //在枚举 k 的时候需要注意:由于 f[i][k] 由 f[i][k - p] 更新得到, //那么就需要保证更新 f[i][k] 的时候 f[i][k - p] 没有被更新, //由于 p 是一个正整数,所以我们倒序枚举 k 即可 //主要问题在于子树上的课只能选一次,如果用小数去更新大数,那么子树上的课会被多选一次 for(int i=min(tot[u],m);i>=1;i--) for(int j=0;j<i;j++) dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]); } } //还得将一颗树转换成森林 int main() { scanf("%d%d",&n,&m); int s; for(int i=1;i<=n;i++) { scanf("%d%d",&s,&a[i]); addedge(s,i); } //树根为0,这样就可以将森林转换为树 m=m+1;//因为加了一个根为0的点 dfs(0); cout<<dp[0][m]; } /** 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 **/