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 ;
}