1.没有上司的舞会
某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
用f[i][0]表示不选择i点时,i点及其子树能选出的最多人数,f[i][1]表示选择i点时,i点及其子树的最多人数。
第二步:确定状态转移方程
f[i][0] = Σ(max (f[j][0], f[j][1]))
f[i][1] = 1+ Σf[j][0]
(j是i的儿子!!)
边界:f[i][0] = 0, f[i][1] = 1 --------i是叶子节点
结果为max(f[root][0], f[root][1])
void dfs(long x) { v[x] = 1; for (long i=1; i<=n; i++) { if ((!v[i]) && (fa[i] == x)) { dfs(i); f[x][0] += max(f[i][1], f[i][0]); f[x][1] += f[i][0]; } } }
2.二叉苹果树
有一棵苹果树,苹果树的是一棵二叉树,共N个节点,树节点编号为1~N,编号为1的节点为树根,边可理解为树的分枝,每个分支都长着若干个苹果,现在要要求减去若干个分支,保留M个分支,要求这M个分支的苹果数量最多。
第一步:确定状态
f[u][j]表示在以u为根的子树保留j个分支可以得到的最大苹果数量
第二步:确定状态转移方程
F[u][j] = max(f[u][k] + f[v][j – k - 1] + W)
v分别是u的儿子,w为u到v边上的苹果数目, k属于[0, j]
void dfs(int u) { vis[u]=1; int i,v,w,j,k,son=0; for(i=head[u];i!=-1;i=e[i].next) { v=e[i].ed;w=e[i].w; if(vis[v]==1)continue; dfs(v); for(k=m;k>=1;k--) { for(j=1;j<=k;j++)//在v节点的子树中选择j条边 if(f[u][k]<f[u][k-j]+f[v][j-1]+w) f[u][k]=f[u][k-j]+f[v][j-1]+w; //u与v有一条边,所以加上dp[v][j-1] } } }
3.Strategic game
一城堡的所有的道路形成一个n个节点的树,如果在一个节点上放上一个士兵,那么和这个节点相连的边就会被看守住,问把所有边看守住最少需要放多少士兵。
第一步:确定状态
f[x][1]以x为根的子树在x上放置的士兵的最少所需的士兵数目
f[x][0]以x为根的子树x上不放置的士兵的最少所需的士兵数目
第二步:确定状态转移方程
f[x][1] =1 + Σ min(f[i][0],f[i][1])
// x上放置的士兵,于是它的儿子们可放可不放!
f[x][0] = Σ f[i][1]
//x上不放置的士兵,它的儿子们都必须放!
(i是x的儿子!!)
结果为min(f[root][0], f[root][1])
void dfs(long x) { v[x] = 1; for (long i=0; i<n; i++) { if ((!v[i]) && (b[x][i])) { dfs(i); f[x][0] += f[i][1]; f[x][1] += min(f[i][0], f[i][1]); } } }
4.Cell Phone Network
给你一棵无向树,问你最少用多少个点可以覆盖掉所有其他的点。(一个点被盖,它自己和与它相邻的点都算被覆盖)
int dp[MAXN][MAXN]; //dp[i][0]表示取i结点时的最小结点数 //dp[i][1]表示不取i结点时,i被其儿子覆盖时的最小结点数 //dp[i][2]表示不选点i,i被其父亲覆盖时的最小结点数 //树形DP状态转移从叶子结点开始向根不断转移,所以用深搜。 //搜到叶子结点就结束。 //转移的时候可以将结点以下给圈起来。 //取与不取,树上背包 int vis[MAXN]; void dfs(int u) { vis[u]=1; int i; int flag=1,tmp=INF; for(i=0;i<G[u].size();i++) { int v=G[u][i]; if(!vis[v]) //额,图的最小支配集吧 { dfs(v); dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2])); dp[u][2]+=min(dp[v][0],dp[v][1]); //不取的话,肯定没有父亲这个选项啊 //父亲只有一个,所以不必做过多的讨论 //儿子比较多,可以取所有儿子,但必须选一个儿子 //但选了一个儿子就要做标记 if(dp[v][0]<=dp[v][1]) { dp[u][1]+=dp[v][0]; flag=0; } else { dp[u][1]+=dp[v][1]; tmp=min(tmp,dp[v][0]-dp[v][1]); } } } if(flag) //还没有选儿子,加上这个差值,补回一个儿子 dp[u][1]+=tmp; return ; }