五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。
已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。
一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。
编程任务:
请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。
输入格式:
第1行 n,表示树中结点的数目。
第2行至第n+1行,每行描述每个通道端点的信息,依次为:该结点标号i(0
对于一个n(0 < n <= 1500)个结点的树,结点标号在1到n之间,且标号不重复。
输出格式:
最少的经费。
如右图的输入数据示例
输出数据示例:
输入样例#1: 复制
6 1 30 3 2 3 4 2 16 2 5 6 3 5 0 4 4 0 5 11 0 6 5 0
输出样例#1: 复制
25
样例说明:在结点2,3,4安置3个保安能看守所有的6个结点,需要的经费最小:25
(以下全部都是对于要覆盖任意一个以x为根的子树来说的)
(其中我们设y节点为y的儿子,fa为x的父亲)
1.x节点被自己覆盖,即选择x点来覆盖x点
2.x节点被儿子y覆盖,即选择y点来覆盖x点
3.x节点被父亲fa覆盖,即选择fa点来覆盖x点
借此三种状态,我们可以设f[x][0/1/2]为让以x为根的子树中的节点全部被覆盖,且x点的被覆盖情况为1/2/3时的最小代价
为了方便,我们不妨设这三种情况分别为:
1.f[x][0]---对应上面的1
2.f[x][1]---对应上面的2
3.f[x][2]---对应上面的3
既然是DP,总是有转移方程的,我们想一下dp方程要如何设计
(1):对应上面的1.
f[x][0]=∑ min(f[y][0],f[y][1],f[y][2]) + val[x]
其中val[x]是选择x点的代价
我们很容易想到,在节点x被选择之后,我们就可以无拘无束了(蛤?),也就是说对于x儿子节点y的状态可以不去考虑,因为x节点被选择之后y节点无论如何也会被覆盖到,所以我们在儿子y的所有状态里取min,累加起来就行了
(2):对应上面的3(先讲3,因为2比较难以理解,放到了后面)
f[x][2]=∑ min(f[y][0],f[y][1])
为什么3情况对应的转移方程要这样写呢?
我们不妨这样理解,对于x节点我们让它的父亲节点fa覆盖它,那么根据我们的状态设计,此时必须要满足以x的儿子y为根的子树之中所有点已经被覆盖
那么这时就转化为一个子问题,要让y子树满足条件,只有两种决策:要么y被y的儿子覆盖,要么被y自己覆盖(即选择y节点),只需要在y的这两种状态取min累加就可以了
(3):对应上面的2(DuangDuangDuang 敲黑板划重点啦)
f[x][1]=∑ min(f[y][0],f[y][1]),如果选择的全部都是f[y][1],要再加上min(f[y][0]-f[y][1])
这又是什么意思呢?真是让人摸不着头发,,,质壁分离(逃
到了这里,我们就要回顾一下我们设计的dp状态了:
设f[x][0/1/2]为让以x为根的子树中的节点全部被覆盖,且x点的被覆盖情况为1/2/3时的最小代价
先提示一下,如果你理解了下面,那么本题是很简单的。。如果你没理解,就返回到这里再看一遍吧,我就在这里等着你
咳咳。。说正经的。。(逃
对于此时的状态,f[x][1]代表对于节点x让x被自己的儿子覆盖,那么和分析(2)一样,都要先满足此时以y的子树已经满足了条件,才能进行转移,这就是前面那部分:∑ min(f[y][0],f[y][1])的来历,那么后面那一长串又是怎么回事呢?
我们可以这样理解,此时既然要保证x点是被自己的儿子覆盖的,那么如果此时y子树已经满足了全部被覆盖,但是y此时被覆盖的状态却是通过y节点自己的儿子达到的,那么x就没有被儿子y覆盖到,那么我们不妨推广一下,如果x所有的儿子y所做的决策都不是通过选择y点来满足条件,那么我们就必须要选择x的一个子节点y,其中y满足f[y][0]-f[y][1]最小,并把这个最小的差值累加到f[x][1]中去,这样才能使得x点被自己的儿子覆盖,状态f[x][1]也才能合理地得到转移
好了,如果你还是没有太懂这个(3)的设计过程,请你回到之前再仔细看几遍
如果你已经理解了上面,那么恭喜你这个题,你已经A掉了
因为转移方程既然有了,那么我们就只需要最后的答案了
由于题目中没有说这棵树的根节点是哪个,所以你可以默认1就是根,或者开一个数组在加边的时候记录一下每个点的入度,最后没有入度的点就是根(但好像没有区别,毕竟我A掉了)
最后答案即为min(f[root][0],f[root][1])
因为根节点没有父亲,所以不需要考虑它的f[root][2]状态
那这样的花。。下面就放一下我丑陋的代码好了(逃
还请dalao们不喜勿喷
PS:代码里也有解释,希望能帮到你更深地理解一下本题
#include
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=3005,INF=100000000;
struct Edge{
int v,nxt;
}e[MAXN<<1];
int f[3][MAXN<<1],h[MAXN<<1],tot,n;
int a[MAXN<<1];
inline void add(int u,int v)
{
e[tot].v=v;
e[tot].nxt=h[u];
h[u]=tot++;
}
void dfs(int u,int fa)
{
int i,flag=0,min_las=INF;
f[0][u]=a[u];
for(i=h[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
f[0][u]+=min(f[0][v],min(f[1][v],f[2][v]));
f[2][u]+=min(f[0][v],f[1][v]);
if(f[0][v]>n;
f(i,1,n){
cin>>u;
cin>>a[u]>>k;
f(j,1,k){
cin>>v;
add(u,v);
add(v,u);
}
}
dfs(1,-1);
cout<