POJ 3352 Road Construction 中一个结论的证明

题面

分析:

很多人都给出了做法,在这里不赘述。大概就是先把桥找出来,然后边双缩点,最后统计新图上的度数。因为缩点后为一棵树,所以度数为1(即为叶子)的点的数目+1再除以2下取整就是答案。

这里主要证明一下为什么是对的。

表达式:$$答案=\lfloor\frac{叶子数+1}{2}\rfloor$$

证明:考虑一棵树中,我们找出带权重心,使得重心下每个子节点的叶子节点数尽量的平均(具体实现不讲了),那么在这棵尽量平均的树上,我们每次取两个根节点下子树不同的叶子节点连边,比如说最左边连最右边,左二连右二……,假如是偶数,那么搞定了。否则,那个点再随便连别的一棵子树的一个叶子(其实不是也行)就OK了。

觉得证明不严谨或者有问题的请指出,共同交流。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cctype>
using namespace std; 

const int maxn=1010;
int n,m,tot,head[maxn],cnt,dfn[maxn],low[maxn],c[maxn],ans,d[maxn];
bool br[maxn<<1],vis[maxn];
struct node
{
	int nxt,to;
}edge[maxn<<1];

int read()
{
	int x=0,f=1;
	char c=getchar();
	while (!isdigit(c))
		f=c=='-'?-1:1,c=getchar();
	while (isdigit(c))
		x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}

void add(int u,int v)
{
	edge[++tot]=(node){head[u],v};
	head[u]=tot;
}

void tarjan(int u,int fa)	//get bridges
{
	int i,v;
	low[u]=dfn[u]=++cnt;
	for (i=head[u];i;i=edge[i].nxt)
	{
		v=edge[i].to;
		if (!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if (low[v]>dfn[u])
				br[i]=br[i^1]=1;
		}
		else
		if (i!=(fa^1))
			low[u]=min(low[u],dfn[v]);
	}
}

void dfs(int u,int co)
{
	c[u]=co;
	int i,v;
	for (i=head[u];i;i=edge[i].nxt)
	{
		v=edge[i].to;
		if (br[i]||c[v])
			continue;
		dfs(v,co);
	}
}

int main()
{
	int i,j,u,v;
	n=read();
	m=read();
	tot=1;
	for (i=1;i<=m;i++)
	{
		u=read();
		v=read();
		add(u,v);
		add(v,u);
	}
	for (i=1;i<=n;i++)
		if (!dfn[i])
			tarjan(i,0);
	cnt=0;
	for (i=1;i<=n;i++)
		if (!c[i])
			dfs(i,++cnt);
	for (i=1;i<=n;i++)
		for (j=head[i];j;j=edge[j].nxt)
			if (c[i]!=c[edge[j].to])
			{
				d[c[i]]++;
				d[c[edge[j].to]]++;
			}
	for (i=1;i<=cnt;i++)
		if (d[i]==2)
			ans++;
	printf("%d\n",(ans+1)/2);
	return 0;
}
posted @ 2019-11-01 11:40  MN2016  阅读(106)  评论(0编辑  收藏  举报