题目链接: https://vijos.org/p/1180
题目大意:选课。只有根课选了才能选子课,给定选课数m, 问最大学分多少。
解题思路:
树形背包。cost=1。
且有个虚根0,取这个虚根也要cost,所以最后的结果是dp[0][m+1]。
本题是cost=1的特殊背包问题,在两个for循环上有一个优化。
for(f+1...j....cost)
for(1....k...j-cost)
其中f为当前已经dfs子结点个数。之所以+1,是因为根要预留一个空间。
f+=dfs(t),dfs(t)返回的是子点t的f+1。
其实可以直接把f+1写成m+1, 不过要多好多次没必要的循环。
这种写法在POJ 1155点数量庞大时,将起决定性作用。
#include "iostream" #include "cstdio" #include "cstring" using namespace std; #define maxn 305 int n,m,root,x; int dp[maxn][maxn],head[maxn],w[maxn],tol; struct Edge { int to,next; }e[maxn]; void addedge(int u,int v) { e[tol].to=v; e[tol].next=head[u]; head[u]=tol++; } int dfs(int root) { int i=root,f=0,cost=1; for(int i=cost;i<=m;i++) dp[root][i]=w[root]; for(int a=head[root];a!=-1;a=e[a].next) { int t=e[a].to; f+=dfs(t); for(int j=f+1; j>=cost; j--) for(int k=1; k<=j-cost; k++) dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[t][k]); } return f+cost; //¸ùÒ²ÏûºÄ1 } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) { scanf("%d%d",&x,&w[i]); addedge(x,i); } dfs(0); printf("%d\n",dp[0][m+1]); }
Accepted, time = 22 ms, mem = 924 KiB, score = 100