POJ1523 SPF

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

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

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

四.代码:

1.DFS(0ms)

#include <cstdio>
#include <iostream>
#include <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");
    }
}



你可能感兴趣的:(POJ1523 SPF)