bzoj 2097: [Usaco2010 Dec]Exercise 奶牛健美操 (二分答案+贪心)

来源:互联网 发布:网站源码网 编辑:程序博客网 时间:2024/04/20 21:05

2097: [Usaco2010 Dec]Exercise 奶牛健美操

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 305  Solved: 150
[Submit][Status][Discuss]

Description

Farmer John为了保持奶牛们的健康,让可怜的奶牛们不停在牧场之间 的小路上奔跑。这些奶牛的路径集合可以被表示成一个点集和一些连接 两个顶点的双向路,使得每对点之间恰好有一条简单路径。简单的说来, 这些点的布局就是一棵树,且每条边等长,都为1。 对于给定的一个奶牛路径集合,精明的奶牛们会计算出任意点对路径的最大值, 我们称之为这个路径集合的直径。如果直径太大,奶牛们就会拒绝锻炼。 Farmer John把每个点标记为1..V (2 <= V <= 100,000)。为了获得更加短 的直径,他可以选择封锁一些已经存在的道路,这样就可以得到更多的路径集合, 从而减小一些路径集合的直径。 我们从一棵树开始,FJ可以选择封锁S (1 <= S <= V-1)条双向路,从而获得 S+1个路径集合。你要做的是计算出最佳的封锁方案,使得他得到的所有路径集合 直径的最大值尽可能小。 Farmer John告诉你所有V-1条双向道路,每条表述为:顶点A_i (1 <= A_i <= V) 和 B_i (1 <= B_i <= V; A_i!= B_i)连接。 我们来看看如下的例子:线性的路径集合(7个顶点的树) 1---2---3---4---5---6---7 如果FJ可以封锁两条道路,他可能的选择如下: 1---2 | 3---4 | 5---6---7 这样最长的直径是2,即是最优答案(当然不是唯一的)。

Input

* 第1行: 两个空格分隔的整数V和S * 第2...V行: 两个空格分隔的整数A_i和B_i

Output

* 第1行:一个整数,表示FJ可以获得的最大的直径。

Sample Input

7 2
6 7
3 4
6 5
1 2
3 2
4 5

Sample Output


2

HINT

Source

Gold

[Submit][Status][Discuss]

题解:二分答案+贪心

先二分一个最大值,将一个点所能到达的树链按长度从大到小排序,如果最长链+次长链>最大值,就将最长链砍断,知道最长链+次长链<=最大值为止,记录封锁道路的条数。

如果记录的条数大于要求的条数,就把左指针右移,否则将右指针左移。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 200003using namespace std;int point[N],next[N],v[N],c[N],tot;int m,n,ans,size,maxn,st[N],f[N];void add(int x,int y){tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=1;tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=1;}int cmp(int x,int y){return x>y;}int dfs(int x,int fa){int cnt=0; f[x]=0;for (int i=point[x];i;i=next[i]) if (v[i]!=fa)  dfs(v[i],x);for (int i=point[x];i;i=next[i]) if (v[i]!=fa)  st[++cnt]=f[v[i]]+1;sort(st+1,st+cnt+1,cmp);if (cnt==0) return f[x]=0;if (cnt==1) if (st[1]>maxn)  {size++;return f[x]=0; } else return f[x]=st[1];int i=2;while (st[i-1]+st[i]>maxn&&i<=cnt) { size++; i++; }if (i==cnt+1&&st[cnt]>maxn) size++;return f[x]=st[i-1];}bool pd(int x){maxn=x;size=0; dfs(1,0);if (size<=m)  return true;else return false;}int main(){scanf("%d%d",&n,&m);for (int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);add(x,y);}    int l=1; int r=n; ans=n;    while (l<=r)    {    int mid=(l+r)/2;    if (pd(mid))  ans=min(ans,mid),r=mid-1;    else l=mid+1;}printf("%d\n",ans);}


0 0
原创粉丝点击