NOIP2012 洛谷P1084 疫情控制
题目:
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,
也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境
城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境
城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,
首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在
一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等
于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
参考链接:https://www.cnblogs.com/linda-fcj/p/7217198.html
分析:二分答案,然后对于每支军队,到根节点的子节点的距离小于mid的全都到根节点的子节点集中,其他军队都尽量往上走,能走到哪就是哪(还要注意,如果一个节点的所有子节点都有军队,那么这个节点也就相当于有军队)。然后将根节点的子节点上的军队剩余时间排序,剩余时间最小且无法做到去一趟根节点再回来的军队驻守在原来的子节点上,其他军队都到根节点上,然后排序,并将所有没有军队的子节点排序,贪心扫描一遍即可(最后判断一下根节点的子节点是不是都有军队,如果都有,就输出时间,否则输出-1)
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define maxn 50010 6 using namespace std; 7 struct node //定义边信息的结构体 8 { 9 int to; 10 int next; 11 int w; 12 }edge[maxn*2]; 13 struct shudui 14 { 15 int gra; //这个gra是根节点下的一个子节点 16 int v; //这个代表距离 17 }extra[maxn],defic[maxn]; 18 int head[maxn*2],arm[maxn],fath[maxn],gran[maxn],dis[maxn],cal[maxn],tim[maxn],b[maxn]; 19 int n,m,tot=0,cnt=0,sum=0,na=0,nc=0; 20 bool cmp(const shudui &a,const shudui &b) //定义排序方式 21 { 22 return a.v>b.v; 23 } 24 void add_edge(int u,int v,int ww) //添加边信息 25 { 26 cnt++; 27 edge[cnt].to=v; 28 edge[cnt].next=head[u]; 29 edge[cnt].w=ww; 30 head[u]=cnt; 31 } 32 33 void Init(int x) 34 { 35 for(int i=head[x];i;i=edge[i].next) 36 { 37 int vv=edge[i].to; 38 if(fath[x]!=vv) 39 { 40 if(x==1) 41 gran[vv]=vv; //gran里面保存的是x这个节点所在树的这一支上的根节点的子节点 42 else 43 gran[vv]=gran[x]; 44 fath[vv]=x; 45 dis[vv]=dis[x]+edge[i].w; 46 Init(vv); 47 } 48 } 49 } 50 51 void dfs(int root) 52 { 53 int maxx=-1,now1=0,now2=0; 54 for(int i=head[root];i;i=edge[i].next) 55 { 56 int vv=edge[i].to; 57 if(fath[root]!=vv) 58 { 59 dfs(vv); //如果tim等于-1,就代表在这个点上要不然就是没有军队驻扎,要不然就是军队都到达了根节点 60 if(tim[vv]==-1) now2=1;//该点尚未被覆盖 61 if(tim[vv]>=edge[i].w) now1=1;//该点可以被覆盖 62 maxx=max(maxx,tim[vv]-edge[i].w); 63 } 64 } 65 if(root!=1&&edge[head[root]].next) //这个root不是根,且不是叶子节点 66 { 67 if(now1) tim[root]=max(tim[root],maxx); 68 else if(now2) tim[root]=max(tim[root],-1); 69 else tim[root]=max(tim[root],0); //这个判断就代表如果root的所有子节点的那颗子树都有军队,那就把父节点标记成也有军队驻扎 70 } 71 } 72 73 void cal_tim(int ti) 74 { 75 for(int i=1;i<=m;i++) 76 if(dis[arm[i]]>=ti) tim[arm[i]]=ti; //tim表示的是这个点最多可以移动到顶点的距离是多少 77 dfs(1); //给tim赋值就表示在这里驻扎的军队不能往树的上一节进发 78 } 79 80 void cal_extra(int ti) 81 { 82 for(int i=1;i<=m;i++) 83 { 84 if(dis[arm[i]]<ti) 85 { 86 extra[++na].gra=gran[arm[i]]; 87 extra[na].v=ti-dis[arm[i]];//extra表示的是根节点可以向下移动的距离 88 } 89 } 90 sort(extra+1,extra+na+1,cmp); 91 for(int i=1;i<=na;i++) 92 { 93 if(!cal[extra[i].gra]) cal[extra[i].gra]=i; //下面else的判断就是如果这个节点已经被覆盖过了,但是之前 94 else if(extra[cal[extra[i].gra]].v>extra[i].v) cal[extra[i].gra]=i;//被覆盖的那个节点更适合用于其他地方,那就换掉它 95 } 96 } 97 98 void cal_defic() 99 { 100 for(int i=head[1];i;i=edge[i].next) 101 { 102 int vv=edge[i].to; //记录一下根节点下的哪一个节点还没有被覆盖 103 if(tim[vv]==-1) 104 { 105 defic[++nc].gra=vv; 106 defic[nc].v=edge[i].w; //这个表示的是这个未被覆盖的节点到达根节点的距离 107 } 108 } 109 sort(defic+1,defic+nc+1,cmp); 110 } 111 /* 112 这个函数作用就是看可不可以通过extra去覆盖defic结构体里面不满足题意的点 113 */ 114 bool remedy(int x) 115 { 116 // if(x==0) 117 // { 118 // printf("%d %d\n",na,nc); 119 // } 120 if(na<nc) return false; 121 memset(b,0,sizeof(b)); 122 int i=1,j=1; 123 for(;i<=nc;i++) 124 { 125 if(!b[cal[defic[i].gra]] && cal[defic[i].gra]) 126 b[cal[defic[i].gra]]=1; //如果这个点在cal_extra函数的时候就已经被分配好了,那就还按原本的 127 else //否则的话这个点之前没有被其他点去覆盖,那就遍历找一个最佳点去覆盖它 128 { 129 while(j<=na && b[j]) j++; 130 if(j>na || extra[j].v<defic[i].v) return false; 131 b[j]=1,j++; 132 } 133 } 134 return true; 135 } 136 137 bool Check(int ti) //检查这个时间是不是可以满足题意 138 { 139 memset(cal,0,sizeof(cal)); 140 na=nc=0; 141 memset(tim,-1,sizeof(tim)); 142 cal_tim(ti); 143 cal_extra(ti); 144 cal_defic(); 145 return remedy(ti); 146 } 147 148 int erfen() //二分枚举题目要求最少时间 149 { 150 int l=-1,r=999999999; 151 while(l+1<r) 152 { 153 int mid=(l+r)/2; 154 if(Check(mid)) r=mid; 155 else l=mid; 156 } 157 return r; 158 } 159 int main() 160 { 161 cin>>n; 162 memset(head,0,sizeof(head)); 163 for(int i=1;i<n;i++) 164 { 165 int x,y,ww; 166 cin>>x>>y>>ww; 167 add_edge(x,y,ww); 168 add_edge(y,x,ww); 169 if(x==1 || y==1) //tot就是根节点下子节点的数量 170 tot++; 171 } 172 cin>>m; 173 if(tot>m) //它的意思是如果军队数量少于这个,那不管时间多大都不会完全覆盖 174 { 175 cout<<-1; 176 return 0; 177 } 178 Init(1); 179 for(int i=1;i<=m;i++) 180 cin>>arm[i]; 181 sort(arm+1,arm+1+m); 182 cout<<erfen(); 183 return 0; 184 } 185 /* 186 函数解释: 187 188 void add_edge(int,int,int);//建图 189 void dfs(int);//处理出要被覆盖的点和已被覆盖的点 190 void cal_tim(int);//处理出每个军队的能力,能到,不能到 191 void cal_extra(int);//能到的还有多少剩余 192 void cal_defic();//不能到的还差多少 193 bool remedy(); //能到的能不能补齐不能到的 194 bool Check(int);//二分的时间是否够(其实就是调用以上四个函数) 195 void Init(int);//处理出各个城市所属的直辖市,根节点的儿子 196 int erfen();//二分时间 197 198 示例: 199 7 200 1 2 1 201 1 3 1 202 2 4 1 203 2 5 1 204 3 6 1 205 3 7 1 206 4 207 4 5 6 7 208 209 输出: 210 0 211 */