洛谷 保安站岗 树形DP

题目地址

[SDOI2006] 保安站岗

题目描述

五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。

一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。

编程任务:

请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。

输入格式

第1行 n,表示树中结点的数目。

第2行至第n+1行,每行描述每个通道端点的信息,依次为:该结点标号i(0

对于一个n(0 < n <= 1500)个结点的树,结点标号在1到n之间,且标号不重复。

输出格式

最少的经费。

如右图的输入数据示例

输出数据示例:

洛谷 保安站岗 树形DP_第1张图片

样例 #1

样例输入 #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为根的树所有结点被覆盖的前提下:
f[x][0]:当前的 x 结点由自身安放的路由覆盖
f[x][1]:当前的 x 结点由子节点的路由覆盖
f[x][2]:当前的 x 结点由父节点的路由覆盖

AC code

#include
#include
#include
#include

using namespace std;
typedef long long ll;

const int N = 1510,inf = 1e9+7;

int n,cnt,h[N],val[N],ne[N<<1],e[N<<1],idx;
int f[N][4];

void add(int a,int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

inline void dp(int x,int fa)
{
	f[x][0] = val[x];
	int sum = 0,mincost = inf;//sum记录有多少个子结点安放路由
//	遍历当前结点的所有子结点
	for(int i = h[x]; ~i; i = ne[i])
	{
		int y = e[i];
		if(y == fa) continue;
		dp(y,x);
		
//		当前结点安放路由覆盖自己,子结点随意
		f[x][0] += min(min(f[y][0],f[y][1]),f[y][2]);
		
//		当前结点被子结点覆盖,即当前结点不安放路由,不存在子结点被父结点覆盖的情况
		f[x][1] += min(f[y][0],f[y][1]);
//		特判:所有字结点都不安放路由的情况
		if(f[y][0] < f[y][1])sum++;
		else
			mincost = min(mincost,f[y][0]- f[y][1]);	
		
//		当前结点由父结点覆盖
		f[x][2] += min(f[y][0],f[y][1]);//子结点就不能为父结点所覆盖了
	}
	if(!sum) 
		f[x][1] += mincost;
}

int main()
{
	cin >> n;
	int x;
	memset(h,-1,sizeof(h));
	for(int i = 1; i <= n; i++)
	{
		cin >> x;
		cin >> val[x];
		int num;
		cin >> num;
		while(num--)
		{
			int y;
			cin >> y;
			add(x,y);
	    	add(y,x);
		}
	}
	
	dp(1,0);
	printf("%d",min(f[1][0],f[1][1]));
	return 0;
}

‍ 大佬题解

你可能感兴趣的:(算法题解,算法,动态规划)