POJ

来源:互联网 发布:淘宝客怎么做推广技巧 编辑:程序博客网 时间:2024/06/15 01:01

It's almost summer time, and that means that it's almost summer construction time! This year, the good people who are in charge of the roads on the tropical island paradise of Remote Island would like to repair and upgrade the various roads that lead between the various tourist attractions on the island.

The roads themselves are also rather interesting. Due to the strange customs of the island, the roads are arranged so that they never meet at intersections, but rather pass over or under each other using bridges and tunnels. In this way, each road runs between two specific tourist attractions, so that the tourists do not become irreparably lost.

Unfortunately, given the nature of the repairs and upgrades needed on each road, when the construction company works on a particular road, it is unusable in either direction. This could cause a problem if it becomes impossible to travel between two tourist attractions, even if the construction company works on only one road at any particular time.

So, the Road Department of Remote Island has decided to call upon your consulting services to help remedy this problem. It has been decided that new roads will have to be built between the various attractions in such a way that in the final configuration, if any one road is undergoing construction, it would still be possible to travel between any two tourist attractions using the remaining roads. Your task is to find the minimum number of new roads necessary.

Input

The first line of input will consist of positive integers n and r, separated by a space, where 3 ≤ n ≤ 1000 is the number of tourist attractions on the island, and 2 ≤ r ≤ 1000 is the number of roads. The tourist attractions are conveniently labelled from 1 to n. Each of the following r lines will consist of two integers, vand w, separated by a space, indicating that a road exists between the attractions labelled v and w. Note that you may travel in either direction down each road, and any pair of tourist attractions will have at most one road directly between them. Also, you are assured that in the current configuration, it is possible to travel between any two tourist attractions.

Output

One line, consisting of an integer, which gives the minimum number of roads that we need to add.

Sample Input
Sample Input 110 121 21 31 42 52 65 63 73 87 84 94 109 10Sample Input 23 31 22 31 3
Sample Output
Output for Sample Input 12Output for Sample Input 20


题意:  输入n,m,表示有n个点,和 m条路,下面m行为m条路,路为双向路,

某个企业想把一个热带天堂岛变成旅游胜地,岛上有N个旅游景点,任意2个旅游景点之间有路径连通(注意不一定是直接连通)。而为了给游客提供更方便的服务,该企业要求道路部门在某些道路增加一些设施。

道路部门每次只会选择一条道路施工,在该条道路施工完毕前,其他道路依然可以通行。然而有道路部门正在施工的道路,在施工完毕前是禁止游客通行的。这就导致了在施工期间游客可能无法到达一些景点。

为了在施工期间所有旅游景点依然能够正常对游客开放,该企业决定搭建一些临时桥梁,使得不管道路部门选在哪条路进行施工,游客都能够到达所有旅游景点。给出当下允许通行的R条道路,问该企业至少再搭建几条临时桥梁,才能使得游客无视道路部门的存在到达所有旅游景点?


这意思就是任意两个景点,至少要存的两条路(不一定是直接连通),这样的互达,才能满足题意,求至少要建的桥梁数;


   其中双连通分量可细分为:点-双连通分量,边-双连通分量。所谓点-双连通分量是指在一个无向图中两点间至少有两条路径,且路径中(不算头尾)的点不同 。不同的点-双连通分量最多有一个公共点,这个点必定是“割顶”。提到割顶不得不在这里啰嗦一下,割顶:就是当删去这个点时,连通块的数量会增加。边-双连通分量是指在一个无向图中两点间至少有两条路径,且路径中的边不同。而桥:是指当删去这个边时,连通块的数量会增加。


这道题的是边双连通分量,求出缩点后图的叶子数,至少添加的边数就是(leaf + 1)/2 ,添加这么边就使图变成边 - 双连通图;

代码:

#include<stdio.h>#include<string.h>#include<vector>#include<stack>#include<algorithm>using namespace std;#define Max 1010int n,m;int dfn[Max];  //时间戳 int low[Max]; //为i或i的子树能够追溯到的最早的栈中节点的次序号int instack[Max]; //  int color[Max]; // 染色 强连通分量,为了进行缩点;int fathar[Max];  // 记录父亲节点,方便查找缩点后的图节点的出度和入度int dfn_num,color_num;vector<int > v[Max];stack<int > s;void init(){int i;//s.clear();for(i=0;i<=n;i++){v[i].clear();dfn[i] = 0;low[i] = 0;color[i] = 0;instack[i] = 0;fathar[i] = 0;}}void tarjan(int x,int fa)   // tarjan算法 {dfn[x] = low[x] = dfn_num++;fathar[x] = fa;        // 方便以后求再缩点之后的节点的度数 s.push(x);int i;for(i=0;i<v[x].size();i++){int k = v[x][i];if(!dfn[k]){tarjan(k,x);low[x] = min(low[x],low[k]);}else if(k!=fa){low[x] = min(low[x],dfn[k]);}}if(low[x]==dfn[x]){color[x] = ++color_num;int t;do{t = s.top();s.pop();color[t] = color_num;}while(t!=x);}}int fff(){int out[Max],i,j;memset(out,0,sizeof(out));for(i=1;i<=n;i++){int k = fathar[i];    // 用fathar[] 数组判断重边;  if(color[i]!=color[k]){out[color[i]]++;out[color[k]]++;}}int con = 0;    //叶子节点数 for(i=1;i<=color_num;i++)if(out[i]==1)con++;return (con+1)/2;  //至少增加的边数; }int main(){int i,j;while(~scanf("%d%d",&n,&m)){init();int x,y;for(i=0;i<m;i++){scanf("%d%d",&x,&y);v[x].push_back(y);v[y].push_back(x);}dfn_num = 1;color_num = 0;for(i=1;i<=n;i++){if(!dfn[i])tarjan(i,i);}printf("%d",fff());}return 0;}


这道题不用判断重复的边也能过,但我还是用fathar[ ] 数组判断了重复边;

不判断重复边的代码:


#include<stdio.h>#include<string.h>#include<vector>#include<stack>#include<algorithm>using namespace std;#define Max 1010int n,m;int dfn[Max];int low[Max];int instack[Max];int color[Max];int dfn_num,color_num;vector<int > v[Max];stack<int > s;void init(){int i;//s.clear();for(i=0;i<=n;i++){v[i].clear();dfn[i] = 0;low[i] = 0;color[i] = 0;instack[i] = 0;}}void tarjan(int x,int fa){dfn[x] = low[x] = dfn_num++;s.push(x);int i;for(i=0;i<v[x].size();i++){int k = v[x][i];if(!dfn[k]){tarjan(k,x);low[x] = min(low[x],low[k]);}else if(k!=fa){low[x] = min(low[x],dfn[k]);}}if(low[x]==dfn[x]){color[x] = ++color_num;int t;do{t = s.top();s.pop();color[t] = color_num;}while(t!=x);}}int fff(){int out[Max],i,j;memset(out,0,sizeof(out));for(i=1;i<=n;i++){for(j=0;j<v[i].size();j++)   //没有判断重复边;{int k = v[i][j];if(color[i] != color[k]){out[color[i]]++;}}}int con = 0;for(i=1;i<=color_num;i++)if(out[i]==1)con++;return (con+1)/2;}int main(){int i,j;while(~scanf("%d%d",&n,&m)){init();int x,y;for(i=0;i<m;i++){scanf("%d%d",&x,&y);v[x].push_back(y);v[y].push_back(x);}dfn_num = 1;color_num = 0;for(i=1;i<=n;i++){if(!dfn[i])tarjan(i,i);}printf("%d",fff());}return 0;}


 


原创粉丝点击