3疫情控制
题目描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,
也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境
城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境
城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,
首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在
一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等
于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从
城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎
的城市的编号。
输出格式:
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
输入输出样例
输入样例#1:
4
1 2 1
1 3 2
3 4 3
2
2 2
输出样例#1:
3
说明
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需
时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0<w <10^5;
对于 60%的数据,2 ≤ n≤1000,0<w <10^6;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10^9。
【思路】
看了别人的思路。二分查找+贪心。
二分时间ans,问题转化为一个判定问题:能否在ans时间将疫情控制。判定用到了贪心,对于一个军队而言,在ans允许的情况下尽可能地向上走,如果能走到root则停止否则驻扎在所能到的最高点。在不超过ans的情况下,向上走的选择一定不差于其他。然后将root的军队分配给需要军队驻扎的子节点即可。需要注意的是,可能有一个结点军队经过它到达了root但是剩余时间不足以使他返回,特殊处理。
只写出了80分的程序==。
经修改,已AC。
需要注意的是特殊处理的是不足以回去的,而不是只要没有军队就把经过的军队叫回去。
【代码】
1 #include<iostream> 2 #include<cstring> 3 #include<vector> 4 #include<set> 5 #include<algorithm> 6 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 7 using namespace std; 8 9 typedef long long LL; 10 const int maxn = 100000+10; 11 const int INF=1<<30; 12 struct Edge{ 13 int v,d,next; 14 }edges[maxn]; 15 int en=0,first[maxn]; 16 vector<int> child[maxn]; 17 multiset<int> S; 18 multiset<int>::iterator it; 19 int P[maxn]; 20 int P_dist[maxn]; //指向父亲的边长 21 int has_army[maxn]; 22 int pos[maxn]; 23 int n,m; 24 25 void AddEdge(int u,int v,int d) { 26 ++en; 27 edges[en].v=v; edges[en].d=d; 28 edges[en].next=first[u]; 29 first[u]=en; 30 } 31 void build_tree(int u,int fa) { //由边的关系建立有根树 32 for(int i=first[u];i>=0;i=edges[i].next) { 33 int v=edges[i].v; 34 if(v!=fa) { 35 P_dist[v]=edges[i].d; 36 P[v]=u; 37 child[u].push_back(v); 38 build_tree(v,u); 39 } 40 } 41 } 42 int dfs_leaf(int u) { //dfs 判断哪一个结点需要军队支援 43 if(has_army[u]) return 1; 44 int d=child[u].size(); 45 if(d==0) return has_army[u]=0; //!has_army && d==0 //leaves 46 int f=1; 47 FOR(i,0,d) { 48 int v=child[u][i]; 49 if(!dfs_leaf(v)) {f=0; if(u!=0) break;} //u==root不能直接break 因为其他子节点的has_army可能还未求出 50 } 51 return has_army[u]=f; 52 } 53 54 int rest_time[maxn]; 55 int special[maxn]; 56 bool can(int ans) { //贪心判断时间 ans 是否可行 57 memset(has_army,0,sizeof(has_army)); 58 memset(special,0,sizeof(special)); 59 FOR(i,0,n) rest_time[i]=INF; 60 S.clear(); //clear 61 62 FOR(i,0,m) { 63 int u=pos[i]; 64 int len=ans; 65 while(u!=0) { 66 if(P_dist[u]>len) { 67 has_army[u]=1; 68 break; 69 } 70 if(P[u]==0) { 71 special[u]=1; rest_time[u]=min(rest_time[u],len-P_dist[u]); 72 } 73 len-=P_dist[u]; 74 u=P[u]; 75 } 76 if(u==0) { //到达root的队伍的剩余时间 77 S.insert(len); 78 } 79 } 80 81 dfs_leaf(0); 82 83 int tot=0; 84 int y[maxn]; 85 FOR(i,0,child[0].size()) { 86 int v=child[0][i]; 87 if(!has_army[v]) { 88 if(special[v] && rest_time[v]<P_dist[v]) { //有士兵从此经过但剩余时间不足以返回 // 只有不足以返回才把士兵叫回 89 S.erase(rest_time[v]); 90 } 91 else y[tot++]=P_dist[v]; 92 } 93 } 94 sort(y,y+tot); 95 for(int i=tot-1;i>=0;i--) { 96 it=S.lower_bound(y[i]); 97 if(it==S.end()) return false; //没有可以到达该节点的 98 S.erase(it); 99 } 100 return true; 101 } 102 int main() { 103 ios::sync_with_stdio(false); 104 cin>>n; 105 memset(first,-1,sizeof(first)); 106 FOR(i,0,n-1) { 107 int u,v,w; cin>>u>>v>>w; 108 --u; --v; 109 AddEdge(u,v,w); 110 AddEdge(v,u,w); 111 } 112 cin>>m; 113 FOR(i,0,m){ 114 cin>>pos[i]; pos[i]--; //pos-- 115 } 116 build_tree(0,-1); 117 if(m<child[0].size()) { cout<<-1; return 0; } //军队数不足 118 int x=0,y=200000; 119 while(x<y) { //二分时间->最小 120 LL mid=x+(y-x)/2; 121 if(can(mid)) y=mid; else x=mid+1; 122 } 123 cout<<x; 124 return 0; 125 }
还有就是不得不说的问题:cin cout在没有加速的情况下真慢。