POJ 3164 Command Network(最小树形图)

来源:互联网 发布:java.util.zip 加密 编辑:程序博客网 时间:2024/06/05 06:03

题目链接:
POJ 3164 Command Network
题意:
给出n个点[下标从1–n]的坐标和m条单向边[i,j]表示可以从i点建一条边到j,权值是两点距离,
求将这n个点连通的最小边权和。如果无法连通输出”poor snoopy”,否则输出最小边权和。

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <climits>#include <cmath>#include <ctime>#include <cassert>#define IOS ios_base::sync_with_stdio(0); cin.tie(0);using namespace std;typedef long long ll;const int MAX_N = 110;const int MAX_M = 10010;const double INF = 1e20;const double eps = 1e-8;int n, m;int NV, NE; //NV和NE分别是结点和边个数(结点下标从0--NV - 1,边下标从0-- NE - 1)int ID[MAX_N], vis[MAX_N], pre[MAX_N]; //结点重新编号后的编号、寻找环时是否访问、结点的前驱结点double  In[MAX_N]; //结点入度最小值struct Point{    double x, y;    Point() {}    Point(double _x, double _y) : x(_x),y(_y) {}    double dis(const Point& rhs) const {        return hypot(x - rhs.x, y - rhs.y);    }}point[MAX_N];struct Edge{    int u, v;    double w;    Edge() {}    Edge(int _u, int _v, double _w) :u(_u), v(_v), w(_w) {} }edge[MAX_M];double Directed_MST(int root){    double res = 0, w;    int u, v;    while(1){        //1.找最小入边        for(int i = 0; i < NV; i++) In[i] = INF; //将所有结点的入度设为-1        for(int i = 0; i < NE; i++){            u = edge[i].u;            v = edge[i].v;            w = edge[i].w;            if(u != v && w < In[v]){ //消除自环影响                In[v] = w;                pre[v] = u;            }        }        for(int i = 0; i < NV; i++){ //检查是否存在除root孤立点,若存在则不存在最小树形图            if(i == root) continue;            if(In[i] == INF) return -1; //i是孤立点        }        //2.找环        int cnt = 0; //记录所有结点重新标号后新结点数量        memset(vis, -1, sizeof(vis));        memset(ID, -1, sizeof(ID));        In[root] = 0; //将根节点入度重设为0        for(int i = 0; i < NV; i++){ //遍历所有结点找环            res += In[i]; //结果加上当前结点的最少入度            v = i;            while(vis[v] != i && ID[v] == -1 && v != root){ //将v所在有向环(或有向单链)上的所有结点收缩为i结点                vis[v] = i;                v = pre[v];            }            if(v != root && ID[v] == -1){ //只有有向环时重新标号                for(int u = pre[v]; u != v; u = pre[u]){ //将i所在有向环上所有结点收缩为结点cnt                    ID[u] = cnt;                }                ID[v] = cnt++;            }        }        if(cnt == 0) break; //只剩下了root所在的有向环        for(int i = 0; i < NV; ++i){            if(ID[i] == -1) ID[i] = cnt++; //root所在有向环上结点或者其他有向环外接单链上点重新标号        }        //3.建新图        for(int i = 0; i < NE; i++){ //更新其他点到环上的距离            u = edge[i].u;            v = edge[i].v;            edge[i].u = ID[u];            edge[i].v = ID[v];            if(edge[i].u != edge[i].v){ //收缩点后不是同一结点                edge[i].w -= In[v];            }        }        NV = cnt; //更新新图的结点数量和根节点编号        root = ID[root];    }    return res;}int main(){    while(~scanf("%d%d", &n ,&m)){        NV = n, NE = m;        for(int i = 1; i <= n; ++i){            scanf("%lf%lf", &point[i].x, &point[i].y);        }        for(int i = 0; i < m; ++i){            int u, v;            scanf("%d%d", &u, &v);            if( u != v) edge[i].w = point[u].dis(point[v]);            else edge[i].w = INF;            edge[i].u = u - 1, edge[i].v = v - 1; //保证结点下标从0--NV-1        }        double ans = Directed_MST(0);        if(fabs(ans + 1) <= eps) printf("poor snoopy\n");        else printf("%.2f\n", ans);    }    return 0;}
0 0
原创粉丝点击