POJ1523_SPF_求割点与分块_tarjan算法

来源:互联网 发布:亚马逊关键词排名优化 编辑:程序博客网 时间:2024/06/06 01:14

【PS:昨晚跟主任讨论的时候才发现自己的代码错了= =没想到错误的代码也能A,只能说明这道题的数据太弱= =为了警示自己,把错误的代码也留下来,附在最后面】


题意:

给一个无向图,求其中的割点,并求当此割点删掉后图中形成多少个连通分量

题解:

用tarjan求割点:伪代码

求割点的方法就用上面的,d[u]和low[u]的含义是:

d[u],dfs到结点u时的时间

low[u],由u在搜索树中的子节点通过非父子边可以追溯到的最早的结点开始的时间

时间:每调用一次dfs函数,时间增加1

原理我就不啰嗦了,看看tarjan算法的证明和实现过程就能大致明白。

关键这里还要求分块,看到网上有解题报告是又dfs一遍根据low数组来求,觉得比较麻烦,想想其实可以不用再dfs了,

在搜索过程中如果当前结点是割点,那么在搜索树中,每一个子节点就是一个连通分量,所以可以通过记录子节点的个数来求分块,非根节点的割点求分块,还需要判断是否父亲节点的一支也是一个连通分量,这时就需要看父亲节点的low值,如果跟所有的孙子结点的low值都不同,那么父亲节点也是一个连通分量


总之这次题编的磕磕绊绊,为了实现上面的思路,过程中又加了非常多的变量,导致我的程序非常的复杂,也不易读,计划等下再改一次程序,用更精炼的方法再做一次。

【PS:这题的输入输出让人非常蛋疼= =】

原题:

SPF
Time Limit: 1000MS
Memory Limit: 10000KTotal Submissions: 4638
Accepted: 2138

Description

Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a failure of a single node, 3, in the network on the left would prevent some of the still available nodes from communicating with each other. Nodes 1 and 2 could still communicate with each other as could nodes 4 and 5, but communication between any other pairs of nodes would no longer be possible.

Node 3 is therefore a Single Point of Failure (SPF) for this network. Strictly, an SPF will be defined as any node that, if unavailable, would prevent at least one pair of available nodes from being able to communicate on what was previously a fully connected network. Note that the network on the right has no such node; there is no SPF in the network. At least two machines must fail before there are any pairs of available nodes which cannot communicate.

Input

The input will contain the description of several networks. A network description will consist of pairs of integers, one pair per line, that identify connected nodes. Ordering of the pairs is irrelevant; 1 2 and 2 1 specify the same connection. All node numbers will range from 1 to 1000. A line containing a single zero ends the list of connected nodes. An empty network description flags the end of the input. Blank lines in the input file should be ignored.

Output

For each network in the input, you will output its number in the file, followed by a list of any SPF nodes that exist.

The first network in the file should be identified as "Network #1", the second as "Network #2", etc. For each SPF node, output a line, formatted as shown in the examples below, that identifies the node and the number of fully connected subnets that remain when that node fails. If the network has no SPF nodes, simply output the text "No SPF nodes" instead of a list of SPF nodes.

Sample Input

1 25 43 13 23 43 501 22 33 44 55 101 22 33 44 66 32 55 100

Sample Output

Network #1  SPF node 3 leaves 2 subnetsNetwork #2  No SPF nodesNetwork #3  SPF node 2 leaves 2 subnets  SPF node 3 leaves 2 subnets

Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time11805500chengtbf1523Accepted212K0MSC++2297B2013-07-18 10:07:45正确代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define N 1003using namespace std;int d[N],low[N];//分别记录每个节点的发现时间和所能到达的最早的结点的时间int dfs_time;int father[N];int root;int visited[N];//记录是否被访问过int subsets[N];//记录被分成的子节点的个数bool flag_cut_node[N];//标记是否为割点vector<int>f[N];//建树专用int case_num=1;int find_min(int a,int b){return a<b?a:b;}void tarjan(int u){visited[u]=1;d[u]=low[u]=++dfs_time;int i,child;for ( i = 0; i< f[u].size(); i++){child=f[u][i];if (!visited[child]){father[child]=u;//这里没有判断是否等于父亲,是因为如果是父亲,一定在发现结点u之前就已经被发现了,不可能进入tarjan(child);low[u]=find_min(low[u],low[child]);//更新此结点if (low[child]>=d[u])//表示子节点没有到达u结点的祖先的路径,那么这个子树就是一个连通分支{subsets[u]++;}}else if (child!=father[u]){low[u]=find_min(low[u],d[child]);//更新u结点}}}void count(){int i,dad;int flag=1;for ( i = 1; i <=1000 ; i++){dad=father[i];if (i==root)//是根节点就看子树是否大于1{if(subsets[i]>1)flag_cut_node[i]=true;}else if( dad!=root && dad!=-1 &&d[dad]<=low[i])//非根节点需要找自己的儿子有没有满足d[u]<=low[child]{flag_cut_node[dad]=true;//这个方法非常的优化,因为只需扫一遍就可以求出所有割点,不需要再深搜了}}printf("Network #%d\n",case_num++);for ( i = 1; i <=1000 ; i++){if (flag_cut_node[i]){if (i!=root){subsets[i]++;//还需要加上父亲结点的那一支}printf("  SPF node %d leaves %d subnets\n",i,subsets[i]);flag=0;//判断是否有割点}}if (flag){printf("  No SPF nodes\n");}printf("\n");}int main(){int a,b,i,flag=1;while (1){for ( i = 1; i <=1000 ; i++){f[i].clear();}memset(subsets,0,sizeof(subsets));memset(d,-1,sizeof(d));memset(low,-1,sizeof(low));memset(visited,0,sizeof(visited));memset(father,-1,sizeof(father));memset(flag_cut_node,0,sizeof(flag_cut_node));dfs_time=0;for(i=1;;i++)//输入{scanf("%d",&a);if (a==0){if (i==1)flag=0;break;}scanf("%d",&b);f[a].push_back(b);//双向存树f[b].push_back(a);}if (!flag)break;for ( i = 1; i <=1000 ; i++){if (f[i].size()!=0)//找到第一个在此连通图中的结点,即为根{root=i;tarjan(root);count();break;}}}return 0;}


【错误】代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define N 1002using namespace std;struct NODE{int node_num;int subnet;}node[N];//记录割点的编号和所划分的块数int dfs_time;//此变量用来存发现时间int node_amount;//存割点数目int child_num[N];//存dfs树中当前结点u的孩子结点个数int relative_num[N];//表示跟他相连的结点个数,跟上面一个有区别int d[N],low[N];//分别为发现时间,其子节点中能到达的最小时间int root;//根节点int visited[N];//判断是否访问过int father[N];bool whether_cut[N];vector<int>f[N];vector<int>find_real_child[N];//存dfs中真正的子节点int find_min(int a,int b){if (a<b)return a;else return b;}void tarjan_dfs(int u){visited[u]=1;d[u]=low[u]=++dfs_time;int i;int child;for ( i = 0; i < relative_num[u]; i++){child=f[u][i];if (!visited[child]){if (child!=father[u]){father[child]=u;}tarjan_dfs(child);low[u]=find_min(low[u],low[child]);child_num[u]++;//此时,child是u在搜索树中的子节点find_real_child[u].push_back(child);}else if(child!=father[u]){low[u]=find_min(low[u],d[child]);}}}void Count(){int i,child,dad,j;int flag;for ( i = 1; i <=1000; i++){dad=father[i];if (dad!=root && dad!=-1 && d[dad]<=low[i]){whether_cut[dad]=true;}}for ( i = 1; i <= 1000; i++){if (whether_cut[i]){flag=1;node[node_amount].node_num=i;node[node_amount].subnet=child_num[i];for ( j = 0; j <find_real_child[i].size() ; j++){child=find_real_child[i][j];if (low[child]==low[father[i]]){flag=0;}}if (flag){node[node_amount].subnet++;}node_amount++;}}if (child_num[root]>=2){node[node_amount].node_num=root;node[node_amount].subnet=child_num[root];node_amount++;}}int cmp(struct NODE a,struct NODE b){return a.node_num<b.node_num;}int main(){int a,b,i,flag=1,case_num=1;while (1){for ( i = 1; i <=1000 ; i++){f[i].clear();find_real_child[i].clear();}memset(relative_num,0,sizeof(relative_num));memset(child_num,0,sizeof(child_num));memset(d,-1,sizeof(d));memset(low,-1,sizeof(low));memset(visited,0,sizeof(visited));memset(father,-1,sizeof(father));memset(node,0,sizeof(node));memset(whether_cut,0,sizeof(whether_cut));dfs_time=0;node_amount=0;for(i=1;;i++)//输入{scanf("%d",&a);if (a==0){if (i==1)flag=0;break;}scanf("%d",&b);f[a].push_back(b);relative_num[a]++;//双向存树f[b].push_back(a);relative_num[b]++;}if (!flag)break;for ( i = 1; i <=1000 ; i++){if (relative_num[i]!=0)//找到第一个在此连通图中的结点,即为根{root=i;tarjan_dfs(root);Count();break;}}sort(node,node+node_amount,cmp);printf("Network #%d\n",case_num++);if (node_amount==0){printf("  No SPF nodes\n");}for ( i = 0; i < node_amount; i++){printf("  SPF node %d leaves %d subnets\n",node[i].node_num,node[i].subnet);}printf("\n");}return 0;}


原创粉丝点击