您的位置:首页 > 其它

【ACM/ICPC2013】树形动态规划专题

2013-08-07 12:47 316 查看

前言:按照计划,昨天应该是完成树形DP7题和二分图、最大流基础专题,但是由于我智商实在拙计,一直在理解树形DP的思想,所以第二个专题只能顺延到今天了。但是昨天把树形DP弄了个5成懂我是很高兴的!下面我把这7题的解题思想和部分代码分享给大家。


题目一:皇宫看守
问题描述:
太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

编程任务:
帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

数据输入:
输入文件中数据表示一棵树,描述如下:
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<i<=n),在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,…,rm。
对于一个n(0 < n<=1500)个结点的树,结点标号在1到n之间,且标号不重复。

数据输出:
输出文件仅包含一个数,为所求的最少的经费。

样例输入:
6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

样例输出:
25

分析:本来这套题里还有一个题目叫“战略游戏”,抽象后的模型跟这题是一样的,遇到那题的时候我没太理解题解是怎么用树形DP做的,于是我就跳过了。后来遇到这题发现不研究的话不行。题目说的很清楚,用最少的点覆盖所有的点,(二分图里有一个模型叫用最少的点覆盖所有的边,以后再议)。如果是一个图的话,它是个NP完全问题,但题目给出的是个树,避免了后效性的问题,所以可以用动态规划来解决。
给出如下定义

F[i,0]表示i点不放,且以i为根节点的子树(包括i节点)全部被观察到;
F[i,1]表示i点不放,且以i为根节点的子树(可以不包括i节点)全部被观察到;
F[i,2]表示i点放,且以i为根节点的子树全部被观察到;
转移如下
1、由F[i,0]定义可知,设j为i的儿子节点,至少要有一个i的儿子节点是放置守卫的,其余的儿子节点可放可不放,但由于根节点i不放,所以其余的儿子节点如果不放的话,必须保证能被观察到,即F[j][0];所以我们需要枚举必须放置的儿子节点,下面的转移方程描述的很清楚:

F[i,0] = min{Sigma(min(F[j][0],F[j,2]))+F[k,2]},其中k为枚举的必放的儿子节点,j为除了k之外的儿子节点
2、由F[i,1]定义可知,i可以被观察到也可以不被观察到,但儿子节点必须都要被观察到,转移如下:

F[i,1] = Sigma(min(F[j,0],F[j,2])) j是i的儿子节点
3、由F[i,2]定义可知,i点放置了守卫,所以对于每个儿子节点都能被观察到,取F[j,0],F[j,1],F[j,2]最小值即可:

F[i,2] = min(F[j,0],F[j,1],F[j,2]) j是i的儿子节点
对于叶节点i,F[i,0] = F[i,2] = data[i],F[i,1] = 0;
看了题解我是恍然大悟啊,智商不够,这个转移方程还是比较难想的。

参考代码

//
//  没有上司的晚会.cpp
//  树形DP
//
//  Created by TimmyXu on 13-8-3.
//  Copyright (c) 2013年 TimmyXu. All rights reserved.
//

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;

const int maxn = 6000+10;

int n,root,du[maxn],data[maxn],f[maxn][2],g[maxn][maxn],x,y;

void doit(int x)
{
f[x][0] = 0;
f[x][1] = data[x];
for (int i = 1;i <= g[x][0];i++)
{
doit(g[x][i]);
f[x][0] += max(f[g[x][i]][0],f[g[x][i]][1]);
f[x][1] += f[g[x][i]][0];
}
return;
}

int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++)
scanf("%d",&data[i]);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(du,0,sizeof(du));
for (int i = 1;i < n;i++)
{
scanf("%d%d",&x,&y);
du[x]++;
g[y][0]++;
g[y][g[y][0]]= x;
}
for (int i = 1;i <= n;i++)
if (du[i] == 0)
{
root = i;
break;
}
doit(root);
printf("%d\n",max(f[root][0],f[root][1]));
}


View Code

总结:树形dp的题目,如果单纯给出节点间矛盾关系,或者在二叉树上做背包,都是比较简单的。我目前遇到的题目中,多叉树背包是一个难点,希望自己能继续学习,能拿下。OK,下午二分图最大流搞起!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: