题目链接

Description

After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.

With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.

Input

The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between node i and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.

Output

For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy’.

Sample Input

4 6
0 6
4 6
0 0
7 20
1 2
1 3
2 3
3 4
3 1
3 2
4 3
0 0
1 0
0 1
1 2
1 3
4 1
2 3

Sample Output

31.19
poor snoopy

分析:

题意:两国交战,一个国家的指挥官要将命令通过传输网络下达到部队,部队通过架设电缆来传输信息。电缆传输信息是单向的,并且命令只要能够传下去就可以,不需要下面的不对向上级汇报。现在给出各个部队的位置和可以建立连接的部队,问至少需要多长的电缆。

理解题目意思后,就转换为给你一个有向图,求有向图的最小树形图。在这里要用到朱刘算法(终于见到我们中国人自己写出的算法了)。具体步骤如下:

基于贪心和缩点的思想。
假设根的顶点是V0。
(1)除了根结点外,所有的点Vi,找到以Vi为终点的最短的边,加入集合中(pre[v]存放的是终点v的起点,In[v]存放终点为v的最短的边)
(2)检查集合中有没有有向环和收缩点。若没有有向环和收缩点,结束计算;若没有有向环、但含收缩边,则跳至步骤(4);若含有有向环,则跳至步骤(3)。
(3)含有有向环,则收缩有向环(缩点),把有向环收缩为一个点,其有向环内的边被收缩掉,而环外的边被保留,构建新图,重复步骤(1)、(2)。
(4)没有有向环,有收缩边,则展开收缩边。

具体的可以根据图来理解一下

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<limits.h>
using namespace std;
const int MAXN = 110;
const int MAXM = 10010;
const int INF=0x3f3f3f3f;
//存储边的信息
struct Node
{
    int from;
    int to;
    double w;
}Edges[MAXM];

//存储点的信息
struct Node1
{
    double x;
    double y;
}Point[MAXN];

//计算两点间的距离,也就是在计算边长
double Dist(Node1 a, Node1 b)
{
    double x = a.x - b.x;
    double y = a.y - b.y;
    return sqrt(x*x+y*y);
}

//pre是i点的起点,
int pre[MAXN],vis[MAXN],flag[MAXN];
double In[MAXN],sum;//IN是在表示以i为终点的最短边长

double ZhuLiu(int root,int N,int M)
{
    sum = 0;
    while(true)
    {
        for(int i = 0; i < N; ++i)
            In[i] = INF;

        for(int i = 0; i < M; ++i)//找到以没个遍为终点的最短边
        {
            int u = Edges[i].from;//起点
            int v = Edges[i].to;//终点
            if(Edges[i].w < In[v] && u != v)
            {
                pre[v] = u;//v为终点,pre[v]存放起点
                In[v] = Edges[i].w;//权值最小的边
            }
        }

        for(int i = 0; i < N; ++i)//如果存在除root以外的孤立点,则不存在最小树形图
        {
            if(i == root)
                continue;
            if(In[i] == INF)
                return -1;
        }

        int CntNode = 0;
        memset(flag,-1,sizeof(flag));//flag是重新的编号
        memset(vis,-1,sizeof(vis));//vis是标记他们在那个环里面
        In[root] = 0;
        for(int i = 0; i < N; ++i)   //找环,标记每个环
        {
            sum += In[i];
            int v = i;
            while(vis[v]!=i && flag[v]==-1 && v!=root)//每个点寻找其前序点,要么最终寻找至根部,要么找到一个环
            {
                vis[v] = i;
                v = pre[v];
            }
            //找到了一个环
            if(v != root && flag[v] == -1)  //新图重新编号
            {
                for(int u = pre[v]; u != v; u = pre[u])
                    flag[u] = CntNode;
                flag[v] = CntNode++;//还是自身的编号,只是在编号的同时实现了编号数目的增加
            }
        }
        if(CntNode == 0)    //无环,跳出
            break;
        for(int i = 0; i < N; ++i)//给那些没有形成环的点也要重新编号
        {
            if(flag[i] == -1)
                flag[i] = CntNode++;
        }
        for(int i = 0; i < M; ++i)  //建立新图,更新其他点到环的距离
        {
            int v = Edges[i].to;
            Edges[i].from = flag[Edges[i].from];
            Edges[i].to = flag[Edges[i].to];
            if(Edges[i].from != Edges[i].to)
                Edges[i].w -= In[v];
        }
        N = CntNode;
        root = flag[root];
    }
    return sum;
}

int main()
{
    int x,y,N,M;
    while(~scanf("%d%d",&N,&M))
    {
        int id = 0;
        for(int i = 0; i < N; ++i)
            scanf("%lf%lf",&Point[i].x,&Point[i].y);
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d",&x,&y);
            if(x == y)
                continue;
            x--;
            y--;
            Edges[id].from = x;
            Edges[id].to = y;
            Edges[id++].w = Dist(Point[x],Point[y]);
        }
        double ans = ZhuLiu(0,N,id);
        if(ans == -1)
            printf("poor snoopy\n");
        else
            printf("%.2lf\n",ans);
    }
    return 0;
}
posted on 2017-10-25 20:52  渡……  阅读(216)  评论(0编辑  收藏  举报