题意:
给一个图,问你最少添加多少条边可以成为一个双连通图(就是去掉任何一条边后图仍然连通)
题解:【摘自北大的集训课件】
只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。
可以求出所有的桥,把桥删掉。然后把所有的连通分支求出来,显然这些连通分支就是原图中的双连通分支。把它们缩成点,然后添上刚才删去的桥,就构成了一棵树。在树上添边使得树变成一个双连通分支即可。
本题只要求输出一共需要添加多少条边,而不需要求具体的方案。其实可以统计度为1的叶子节点(设共有x个),然后直接输出(x+1)/2即可
命题:一棵有n(n>=2)个叶子结点的树,至少(只需)要添加ceil(n/2)条边,才(就)能转变为一个没有桥的图。或者说,使得图中每条边,都至少在一个环上。
证明:
这里只证明n为偶数的情况。n为奇数的证明类似。
先证明添加n/2条边一定可以达成目标。
n=2时,显然只需将这两个叶子间连一条边即可。命题成立。
设n=2k(k>=1)时命题成立,即AddNum(2k)=k。下面将推出n=2(k+1)时命题亦成立
n=2k+2时,选取树中一条迹(无重复点的路径),设其端点为a,b;并设离a最近的度>=3的点为a',同理设b'。
(关于a‘和b’的存在性问题:由于a和b的度都为1,因此树中其它的树枝必然从迹<a,b>之间的某些点引出。否则整棵树就是迹<a,b>,n=2<2k+2,不可能。)
a’ b’不重合时:
在a,b间添一条边,则迹<a,b>上的所有边都已不再是桥。这时,将刚才添加的边,以及aa‘之间,bb’之间的边都删去,得到一棵新的树。因为删去的那些边都已经符合条件了,所以在之后的构造中不需要考虑它们。由于之前a‘和b’的度>=3,所以删除操作不会使他们变成叶子。因此新的树必然比原树少了两个叶子a,b,共有2k个叶子。由归纳知需要再加k条边。因此对n=2k+2的树,一共要添加k+1条边。
a’ b’重合时:
将a和一个非b的叶子节点x连上,然后将环缩点至a’。
因为叶子节点是偶数,所以必然还存在一个非b非x的叶子节点不在环上,因此a’不会变成叶子节点,于是新图比原图少2个叶子节点。
再证明n/2是最小的解。
显然,只有一个叶子结点被新加的边覆盖到,才有可能使与它相接的那条边进入一个环中。而一次加边至多覆盖2个叶子。因此n个叶子至少要加n/2条边。
证毕。
【PS:其实本题的数据比较弱,我是按照一种很简化的方法做的,即默认low值相同的点属于同一连通分量,low值不同的两点间的边就是割边,但是其实是不等价的,POJ的discuss里面有人讨论到一个图就是反例。不过此题数据比较水,就这么给水过了】
原题:
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 7302 | Accepted: 3184 |
Description
Given a description of the current set of R (F-1 <= R <= 10,000) paths that each connect exactly two different fields, determine the minimum number of new paths (each of which connects exactly two fields) that must be built so that there are at least two separate routes between any pair of fields. Routes are considered separate if they use none of the same paths, even if they visit the same intermediate field along the way.
There might already be more than one paths between the same pair of fields, and you may also build a new path that connects the same fields as some other path.
Input
Lines 2..R+1: Each line contains two space-separated integers which are the fields at the endpoints of some path.
Output
Sample Input
7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7
Sample Output
2
Run ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
11810196 | chengtbf | 3177 | Accepted | 320K | 0MS | C++ | 1540B | 2013-07-18 19:45:40 |
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 5005
using namespace std;
int dfn[N],low[N];
int dfs_time,visited[N];
int deg[N];//储存每个缩点的度数,缩点是将一个连通分量看作一个结点
vector<int>f[N];
int find_min(int a,int b)
{
return a<b?a:b;
}
void tarjan(int u,int father)
{
visited[u]=1;
dfn[u]=low[u]=++dfs_time;
int i,child;
for ( i = 0; i < f[u].size(); i++)
{
child=f[u][i];
if (!visited[child])
{
tarjan(child,u);
low[u]=find_min(low[u],low[child]);
}
else if (child!=father)
{
low[u]=find_min(low[u],dfn[child]);
}
}
}
int main()
{
int n,r,i,j,a,b,flag;
int ans,num_deg_1;
int u,v;
while (scanf("%d%d",&n,&r)!=EOF)
{
for ( i = 0; i <=n ; i++)
{
f[i].clear();
}
memset(visited,0,sizeof(visited));
memset(deg,0,sizeof(deg));
dfs_time=0;
ans=0;
num_deg_1=0;
for ( i = 1; i <= r; i++)
{
scanf("%d%d",&a,&b);
flag=1;
for ( j = 0; j < f[a].size(); j++)//考虑是否有重边,如果有的话,就不存进vector中
{
if (f[a][j]==b)
{
flag=0;
break;
}
}
if (flag)
{
f[a].push_back(b);
f[b].push_back(a);
}
}
tarjan(1,-1);
for ( i = 1; i <=n ; i++)
{
u=i;
for ( j = 0; j <f[i].size() ; j++)
{
v=f[i][j];
if (low[u]!=low[v])
{
deg[low[u]]++;
deg[low[v]]++;
}
}
}
for ( i = 1; i <= n; i++)
{
if (deg[i]==2)
{
num_deg_1++;
}
}
ans=(num_deg_1+1)/2;
printf("%d\n",ans);
}
return 0;
}