洛谷1084 疫情控制

题目二:疫情控制
一、问题描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,
也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市
(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市
的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首
都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可
以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个
城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道
路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

输入格式
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u,v,w,每两个整数之间用一个空格隔开,表示从城市 u
到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的
城市的编号。

输出格式
一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

二、需求分析
分析:首先明白目的是什么:求把首都(1节点)围住的最少时间,注意:不一定非要到根节点的儿子节点才可以,把这一条路封住就可以了。
所以二分一个时间后,是这个点尽量往上走(尽量接近首都),这里会出现两种情况:1、二分的时间不够到达首都,在中途停了下来,停下来的点设为访问过。2、二分的时间足够到达首都。对于第一种情况,因为这个点没有到首都,所以进行标记上传。至于什么作用,接着往下看。
对于第二种情况,这个点到达了首都,它就有两种选择,一是回到它来的路径上那个最靠近首都的点,二是跨过根节点去覆盖其他的点。
说到这里,就很容易理解标记上传了,因为第一种情况中说到了,这个点没有到达首都,它可能是首都的儿子,也可能是首都的孙子,更可能是距离首都很远的一个点,但是它算在的这条路径已经是被这个点覆盖了(疫情不会从这里扩散出去!),然而结构体c仅仅扫描了首都的儿子节点,所以在这条路径上的首都的儿子可能没有覆盖,所以写一个函数color把这个点的情况传给它所在路径上的首都的儿子,也就是所谓的标记上传。
我这里主要分了三种情况,情况1.两边均有军队,且不用跨根节点
// 2.只有一边有军队
// 3.两边均有军队,但跨节点的距离最短
三、概要设计
struct Node //节点类
{
int w; //序号

}c[20];

void Add_Edge(int u, int v, int w) {//添加由u到v,权值为w的边
e[u][++tot[u]].to = v; //添加到v的边
e[u][tot[u]].value = w;
e[u][tot[u]].next = head[u]; //.next其实是将tot递减打印u节点的边,head[u]=tot[u]-1
head[u] = tot[u];
}

void read(int& x) { //读取数据,以ASCI读取,确保数据无误
x = 0;
int f = 1;
char c = getchar();
while (c > ‘9’ || c < ‘0’) { if (c == ‘-’) f = -1; c = getchar(); }
while (c >= ‘0’ && c <= ‘9’) { x = x * 10 + c - ‘0’; c = getchar(); }//注意c要减“0”
x *= f;
}

void DFS(int u) //深度遍历u的后续节点更新一阶父节点,深度,距离
{
for (int i = head[u],v;i;i = e[u][i].next)
{

	v= e[u][i].to;
	if (1)
	{
		fa[v][0] = u, dep[v] = dep[u] + 1, dis[v][0] = e[u][i].value;
		DFS(v);
	}
	
}

}

void init()//由局部到整体,导出i节点到其上所有节点 j为层数-1
{
for (int i = 1;i <= n;++i)
{
for (int j = 1;j <= dep[i]-2;++j)
{
//fa[i][j] = fa[fa[i][j - 1]][j - 1];
fa[i][j] = fa[fa[i][j - 1]][0];
dis[i][j] = dis[i][j - 1] + dis[fa[i][j - 1]][0];//dis是纵向的父代距离0,1对应第几代

	}
}

}

void UpDate(int x) {//安全性检验;x以下是否安全,x安全则vis[x] = 1
int p = 1, q = 0;
for (int i = head[x];i;i = e[x][i].next)
if (e[x][i].to !=0)
{
UpDate(e[x][i].to);
p = p & vis[e[x][i].to]; //子安全则父安全
q = 1;
}

if (p && q && x != 1)
	vis[x] = 1;

}

int max(int b[],int n)
{
int j = b[1];
for (int i = 1;i <= n;i++)
{
if (j < b[i]) j=b[i];
}
return j;
}

int min(int b[], int n)
{
int j = b[1];
for (int i = 1;i <= n;i++)
{
if (j > b[i]) j = b[i];
}
return j;
}

void Judge()//将军队在的地方设为安全
{
for (int i = 1;i <= m;i++)
{
if (a[i] != 1)
{
vis[a[i]] = 1;
}

}

}

void copy(int a[], int b[], int n)
{
for (int i = 1;i <= n;i++)
{
a[i] = b[i];
}
}

具体代码https://download.csdn.net/download/m0_48134027/20719839;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&露从今夜白

长风破浪会有时,直挂云帆济沧海

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值