POJ 3164:Command Network(最小树形图)

Command Network
Time Limit: 1000MS Memory Limit: 131072K
Total Submissions: 18050 Accepted: 5153

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


题目翻译:

给出N个点M条边,节点的编号从1~N,然后接下来N行给出每个点的坐标。然后给出M条有向边。点与点之间的距离是边的权值。然后根节点永远是1。求将所有点

连接起来的用的最小网线长度。因为战争原因网线稀缺,不要求每两个点直接连通。只要间接连通即可。

解题思路:

知道题目意思后就会明白这个题目是给出定跟1,求这个图的最小生成树。又学新知识了,国产算法,朱刘算法。原来只会求无向图的最小生成树,用prime或克鲁斯卡尔,

现在要求定根的最小生成树,就要用朱刘算法。还是要先看别人的模板,但是他们没怎么加注释(很头痛,因为有些地方看不懂)。所以就copy别人代码仔细看了看。

给他们的代码中的每个变量加了注释。POJ这个题最后输出要用%f输出,本来最后保留两位小数,自己就用了%lf输出,提交就一直wa,就以为自己模板套错了。整个

下午去疯狂的改代码,直到真的该不出错误了。就想着去讨论区看看有没有什么测试数据可以测测,后来看到有个人说输出时候的问题,改了一下提交就过了。感觉这

点真的好坑,用%f输出就正确,%lf输出就错误。像遇到这种问题,自己真的就想不到。


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#define inf 0x3f3f3f3f

using namespace std;

const int maxn = 102;
struct point  ///每一个节点的坐标
{
    double x;
    double y;
}p[maxn];
double Dist(double x1,double y1,double x2,double y2)
{
    double dis = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
    return dis;
}
struct Edge
{
    int u;
    int v;
    double weight;
}edge[maxn*maxn];
///closest[i]中存放距离i最近的节点,vis是标记数组,id[i]存放节点i新的编号,in[i]i的最小的入边权值
int closest[maxn],vis[maxn],id[maxn];
double in[maxn];
///三个参数分别是根节点编号,节点数量,边数量
double Min_Length(int root,int nodenum,int edgenum)
{
    double ans = 0;   
    int i;
    while(true)
    {
        for(i = 1; i <= nodenum; i++)  ///初始化所有节点的最小的入边权值都是无穷大
            in[i] = inf;
        for(i = 0; i < edgenum; i++)   ///遍历edgenum条边
        {
            int u = edge[i].u;
            int v = edge[i].v;
            ///边u->v的权值如果比当前v的最小的入边权值还小,则更新最小权值,更新距离节点v最近的节点是u
            if(u != v && edge[i].weight<in[v])
            {
                in[v] = edge[i].weight;
                closest[v] = u;         ///u是距v最近的点。
            }
        }///找每个节点入边的最小权值。
        /**除了树根以外,存在一个边没有入边,则不能挑出一些边使nodenum个节点连在一起。
        不存在最小树形图**/
        for(i = 1; i <= nodenum; i++)
        {
            if(i == root) continue;
            if(in[i] == inf) return -1;
        }
        ///2.是否存在环
        int cnt = 0;                ///cnt为节点的新编号
        memset(vis,-1,sizeof(vis)); 
        memset(id,-1,sizeof(id));   ///id[i]存放节点i的新编号
        in[root] = 0;               
        for(i = 1; i <= nodenum; i++)
        {
            ans += in[i];    ///第一轮的时候,会把每个节点的最小入边都加到里面。后面可能会删除边的。
            int v = i;
            ///从v一直往前找,如果有环则必回到自身,如果无环应该找到根
            while(v!=root && vis[v]!=i && id[v]==-1)
            {
                vis[v] = i;             ///标记
                v = closest[v];         ///继续往前找
            }
            if(v!=root && id[v]==-1)
            {
                ++cnt;                  ///先加加,因为节点编号从1开始
                id[v] = cnt;
                ///将在一个环中的点缩成一个点,在一个环中的点的cnt(编号)都是相同的。
                for(int u = closest[v]; u != v; u = closest[u])
                {
                    id[u] = cnt;
                }
            }
        }
        if(cnt == 0)  ///cnt为0,代表无环,如果有环必进if条件,进if条件cnt比仍然加加。
            break;
        for(i = 1; i <= nodenum; i++) ///现在为不在环中的节点进行编号
        {
            if(id[i]==-1)
                id[i] = ++cnt;
        }
        ///构建新的图。
        for(i = 0; i < edgenum; i++)
        {
            int u = edge[i].u;   ///先保存原来节点的编号
            int v = edge[i].v;
            edge[i].u = id[u];   ///新图中u,v的编号
            edge[i].v = id[v];
            if(id[u] != id[v])   ///u和v不在一个环中
                edge[i].weight = edge[i].weight - in[v]; ///删边重头戏,这句代码很难懂,一定要好好研究。
        }
        nodenum = cnt;   ///更新节点数量。
        root = id[root];  ///更新根节点的编号
    }
    return ans;
}
int main()
{
    int N,M;
    ///N个节点,编号为1~N,M条边
    while(~scanf("%d%d",&N,&M))
    {
        for(int i = 1; i <= N; i++)  ///获取第i个节点的坐标
            scanf("%lf%lf",&p[i].x,&p[i].y);
        for(int i = 0; i < M; i++)   ///输入M条边
        {
            scanf("%d%d",&edge[i].u,&edge[i].v);
            int u = edge[i].u;        ///点u
            int v = edge[i].v;        ///点v
            if(u == v)                ///节点自己指向自己,去除自环
                edge[i].weight = inf;
            else
                edge[i].weight = Dist(p[u].x,p[u].y,p[v].x,p[v].y);
        }
        double ans = Min_Length(1,N,M);
        if(ans < 0)
            printf("poor snoopy\n");
        else
            printf("%.2f\n",ans);
    }
    return 0;
}

下面举一个例子:辅助理解










  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值