CH Round #72树洞[二分答案 DFS&&BFS]
树洞 CH Round #72 - NOIP夏季划水赛
描述
在一片栖息地上有N棵树,每棵树下住着一只兔子,有M条路径连接这些树。更特殊地是,只有一棵树有3条或更多的路径与它相连,其它的树只有1条或2条路径与其相连。换句话讲,这些树和树之间的路径构成一张N个点、M条边的无向连通图,而度数大于2的点至多有1个。
近年以来,栖息地频繁收到人类的侵扰。兔子们联合起来召开了一场会议,决定在其中K棵树上建造树洞。当危险来临时,每只兔子均会同时前往距离它最近的树洞躲避,路程中花费的时间在数值上等于距离。为了在最短的时间内让所有兔子脱离危险,请你安排一种建造树洞的方式,使最后一只到达树洞的兔子所花费的时间尽量少。
输入格式
第一行有3个整数N,M,K,分别表示树(兔子)的个数、路径数、计划建造的树洞数。
接下来M行每行三个整数x,y,表示第x棵树和第y棵树之间有一条路径相连。1<=x,y<=N,x≠y,任意两棵树之间至多只有1条路径。
输出格式
一个整数,表示在最优方案下,最后一只到达树洞的兔子所花费的时间。
样例输入
5 5 2 1 2 2 3 3 1 1 4 4 5
样例输出
1
数据范围与约定
- 对于20%的数据,1 ≤ n ≤ 10。
- 对于另外30%的数据,每棵树至多与2条路径相连。
- 对于另外30%的数据,保证存在一种最优解,使与3条或更多路径相连的树上一定建造了树洞。
- 对于100%的数据,1 ≤ n ≤ 2000,n-1<=m<=n*(n-1)/2。
一开始读错题
官方题解:
二分答案。
枚举距离特殊点最近的建造的树洞是哪一个,记为X。
在图中删除能够在二分的时间内到达该树洞X的所有点。
此时图变为若干条独立的链,直接求最少需要的树洞数。
在所有枚举的情况中取最小值,与K比较确定二分范围变化。
其实这就是一条链,只不过有一个点可能连出多条链或者连乘环
一条链的话,答案就是(n-t)/(2*t)上取整
很明显可以二分最大时间,也就是节点的最短距离
把特殊点设为root,枚举从能覆盖root的点中选一个建造树洞,
剩下的没vis的全成了链,每条链每2*mid+1必定放一个树洞
注意cnt要+1,一开始建了一个嘛
// // main.cpp // ch72c // // Created by Candy on 26/10/2016. // Copyright © 2016 Candy. All rights reserved. // #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=2010,INF=1e9; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n,m,t,x,y; struct edge{ int v,ne; }e[N<<1]; int h[N],cnt=0; void ins(int u,int v){ cnt++; e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt; cnt++; e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt; } int root=0,de[N]; int d[N],q[N],head=1,tail=0; void bfs(int s){ memset(d,-1,sizeof(d)); head=1;tail=0; q[++tail]=s; d[s]=0; while(head<=tail){ int u=q[head++]; for(int i=h[u];i;i=e[i].ne){ int v=e[i].v; if(d[v]==-1){ d[v]=d[u]+1; q[++tail]=v; } } } } int vis[N],len; void dfs(int u){ vis[u]=1;len++; for(int i=h[u];i;i=e[i].ne){ int v=e[i].v; if(vis[v]) continue; dfs(v); } } int solve(int dis){ int ans=INF; for(int x=1;x<=n;x++){ bfs(x); int cnt=0; if(d[root]>dis) continue; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) if(d[i]<=dis) vis[i]=1; for(int i=1;i<=n;i++) if(!vis[i]){ len=0; dfs(i); cnt+=(len-1)/(2*dis+1)+1; } ans=min(ans,cnt+1);//!!!cnt+1 } return ans; } int main(int argc, const char * argv[]) { n=read();m=read();t=read(); for(int i=1;i<=m;i++){x=read();y=read();de[x]++;de[y]++;ins(x,y);} for(int i=1;i<=n;i++) if(de[i]>3) {root=i;break;} if(!root){ printf("%d",(n-t-1)/(2*t)+1); return 0; } if(n==t){printf("0");return 0;} int l=1,r=n,ans=INF; while(l<=r){ int mid=(l+r)/2; if(solve(mid)<=t) ans=min(ans,mid),r=mid-1; else l=mid+1; } printf("%d",ans); return 0; }
Copyright:http://www.cnblogs.com/candy99/