您的位置:首页 > 其它

POJ1523 SPF

2016-05-05 15:08 323 查看
一.原题链接:http://poj.org/problem?id=1523

二.题目大意:给一个网络,节点与节点相连,让你求去掉某些节点能不能使得整个网络不联通。

三.解题思路:就是求关节点。(去掉使得图不联通的点)

1.直接DFS,枚举删除每个点,看需要dfs多少次才能把所有点遍历完,注意点标号不连续。居然也是0ms。时间复杂度O(n^3),邻接表其实也优化不了稠密图。

2.Tarjan算法,其实就是记忆化搜索的思路。

原理:对于一个图,对它进行深度优先搜索之后,会产生一颗深度优先搜索树(其实就是搜索的路径)。

节点是关节点的充要条件是:

1.它是树的根节点并有2个以上子女。(很明显如果这个点没了,树左右2遍就断开了,记得是搜索树的子女,不是该节点有几个邻接点,邻接点不一定是搜索树的子女)

2.它不是根节点,但是以它出发向下的路径,没有节点能够返回在当前节点之前的点。

第一点好判断,第二点怎么判断呢?

我们引入一个dfn[]数组记录搜索的次序,祖先一定大于子女,

然后再引入一个low[]数组,(作用为记录其可以直接或间接访问的最上层的顶点)使其为本身的dfn[]、能通过当前节点返回其祖先或其祖先之上的节点的dfn[]和他子女的low[]的最小值,这样就能记录整条路径有没有回路了。

于是条件2转化为u不是根节点,并且存在子女v,使得low[v]>=dfn[u],只要看子女就行,因为low[]记录了整条路径的最小值。并且有多少个子女满足就分成多少块。

整个过程在DFS中进行,利用回溯进行low值的确定实在是太精妙了。时间复杂度网上很多都说O(n+m),顶点数+边数,因为他们认为扫所有的点,扫所有的边。我觉得这忽略了从一个点出发,扫其余点时候所需要的判断,稠密图邻接表也没用,不过这题数据很弱。

这题我用的tarjan其实是假设在搜索树的每个顶点都有一条回边回到他的直接祖先。(都有重边)不然的话我就要标记每条边,看看是否走过,因为不管是邻接表还是邻接矩阵,都无法确定是通过原来这条边访问节点的呢,还是有一条成环的边。就是我假设如下图:


这并不影响算法的正确性,因为2个点本来就相连,我加边或者不加边,一个点失去之后,该断的还是会断。但是如果求割边就不一样的。

网上许多代码都是这样的,只要是没标记边的,不信你手动模拟tarjan算法,然后打印每个low[]值看看。

四.代码:

1.DFS(0ms)

#include <cstdio>
#include <iostream>
#include
4000
<cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

const int MAX_SIZE = 1002,
INF = 0x3f3f3f3f;

struct elem
{
int id, cnt;
};
vector <int> G[MAX_SIZE];
vector <elem> SPF;
bool visited[MAX_SIZE], isNode[MAX_SIZE];
int nodeNum = 0, del;

void addEdge(int u, int v)
{
G[u].push_back(v);
G[v].push_back(u);
nodeNum = max(nodeNum, max(u, v));
isNode[u] = isNode[v] = true;
}

void init()
{
int i;
for(i = 1; i <= 1000; i++)
G[i].clear();
SPF.clear();
memset(isNode, 0, sizeof(isNode));
}

void dfs(int u)
{
visited[u] = true;
int i, v;
for(i = 0; i < G[u].size(); i++){
v = G[u][i];
if(v != del && !visited[v]){
dfs(v);
}
}
}

int main()
{
//freopen("in.txt", "r", stdin);

int u, v, i, j, kase = 1, cnt;

while(1){
init();
scanf("%d", &u);
if(0 == u)
break;
scanf("%d", &v);
addEdge(u, v);
while(1){
scanf("%d", &u);
if(0 == u)
break;
scanf("%d", &v);
addEdge(u, v);
}

for(i = 1; i <= nodeNum; i++){
if(!isNode[i])
continue;
del = i;
memset(visited, 0, sizeof(visited));
cnt = 0;
for(j = 1; j <= nodeNum; j++){
if(j != del && !visited[j] && isNode[j]){
dfs(j);
cnt++;
}
}
if(cnt > 1){
elem tmp;
tmp.id = del, tmp.cnt = cnt;
SPF.push_back(tmp);
}
}

printf("Network #%d\n", kase++);
if(0 == SPF.size())
printf("  No SPF nodes\n");
else
for(i = 0; i < SPF.size(); i++)
printf("  SPF node %d leaves %d subnets\n", SPF[i].id, SPF[i].cnt);

printf("\n");
}
}

2.Tarjan

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

const int MAX_SIZE = 1002,
INF = 0x3f3f3f3f;

class Tarjan
{
public:
vector <int> edges[MAX_SIZE];
int low[MAX_SIZE], dfn[MAX_SIZE], root,
maxNode, subnetsNum[MAX_SIZE];
bool visited[MAX_SIZE], isNode[MAX_SIZE],
isSPF[MAX_SIZE], found;

void init()
{
int i;
for(i = 0; i < MAX_SIZE; i++)
edges[i].clear();
found = false;
memset(subnetsNum, 0, sizeof(subnetsNum));
memset(isNode, 0, sizeof(isNode));
memset(visited, 0, sizeof(visited));
memset(isSPF, 0, sizeof(isSPF));
maxNode = 0;
}

void addEdge(int u, int v)
{
edges[u].push_back(v);
edges[v].push_back(u);
maxNode = max(maxNode, max(u, v));
isNode[u] = isNode[v] = true;
}

void dfs(int u, int depth)
{
visited[u] = true;
low[u] = dfn[u] = depth;
int i, v;
for(i = 0; i < edges[u].size(); i++){
v = edges[u][i];
if(!visited[v]){
if(u == root)
subnetsNum[root]++;
dfs(v, depth + 1);
if(u != root)
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u] && u!= root){
isSPF[u] = true;
subnetsNum[u]++;
found = true;
}
}
else
low[u] = min(low[u], dfn[v]);
}
}

void startDFS()
{
for(root = 1; root <= maxNode; root++)
if(isNode[root])
break;
dfs(root, 1);
if(subnetsNum[root] > 1){
isSPF[root] = true;
subnetsNum[root]--;
found = true;
}
}
}G;

int main()
{
//freopen("in.txt", "r", stdin);

int u, v, i, j, kase = 1, cnt;

while(1){
G.init();
scanf("%d", &u);
if(0 == u)
break;
scanf("%d", &v);
G.addEdge(u, v);
while(1){
scanf("%d", &u);
if(0 == u)
break;
scanf("%d", &v);
G.addEdge(u, v);
}

G.startDFS();

printf("Network #%d\n", kase++);
if(!G.found)
printf("  No SPF nodes\n");
else
for(i = 1; i <= G.maxNode; i++)
if(G.isSPF[i])
printf("  SPF node %d leaves %d subnets\n", i, G.subnetsNum[i]+1);

printf("\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: