【佛山市选2013】树环转换
Description
给定一棵N个节点的树,去掉这棵树的一条边需要消耗值1,为这个图的两个点加上一条边也需要消耗值1。树的节点编号从1开始。在这个问题中,你需要使用最小的消耗值(加边和删边操作)将这棵树转化为环,不允许有重边。
环的定义如下:
(1)该图有N个点,N条边。
(2)每个顶点的度数为2。
(3)任意两点是可达的。
树的定义如下:
(1)该图有N个点,N-1条边。
(2)任意两点是可达的。
Input
第一行是一个整数N代表节点的个数。
接下来N-1行每行有两个整数U, V(1 ≤ U, V ≤ N),表示双向边(U, V)
Output
输出把树转化为环的最小消耗值。
Sample Input
4
1 2
2 3
2 4
Sample Output
3
Data Constraint
对于20%的数据,有1≤N≤10。
对于100%的数据,有1≤N≤1000000。
反思&题解
比赛思路: 忘了……
正解思路: 我们考虑贪心的思想:去掉多的边补齐少的边,很显然最优肯定是他们一一对应,所以最后结果就是去掉的边*2再加上树比环少的那条边。对于一个叶子节点,我们就不用去掉这条边了;对于一个节点有两个及以上个儿子的,就要去掉只剩1条(根节点的话可以剩2条),不过如果它一个儿子被去掉过边,那这个儿子就不用算了。运用这种思想遍历一棵树统计答案就行了;
不过GMOJ的评测机会爆栈,所以要用人工栈或者BFS
(据说树形DP才是最正的正解,不过贪心能过为啥要DP呢)
反思: 对于树的各种性质还是要多了解
CODE
#include<bits/stdc++.h>
using namespace std;
struct arr
{
int to,next;
}edge[2000005];
int n,head[2000005],cnt,ans,d[4000005],fa[2000005],tot[2000005];
bool bz[2000005],flag[2000005];
void add(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
int bfs()
{
int h=0,t=1;
d[1]=1;
bz[1]=true;
while (h<t)
{
int i,u=d[++h];
for (i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if (v!=fa[u])
{
d[++t]=v;
fa[v]=u;
}
}
}
int i;
for (i=t;i>=1;i--)
printf("%d\n",d[i]);
for (i=t;i>=1;i--)
if (tot[d[i]]<2) tot[fa[d[i]]]++;
memset(d,0,sizeof(d));
h=0;
t=1;
d[1]=1;
while (h<t)
{
int u=d[++h],i;
if (tot[u]>=2)
{
ans+=tot[u]-1;
if (u==1) ans--;
}
for (i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if (v!=fa[u]) d[++t]=v;
}
}
}
int main()
{
scanf("%d",&n);
int i;
for (i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
bfs();
printf("%d\n",ans*2+1);
return 0;
}