POJ 3164 Command Network(最小树形图)

题目链接:点击打开链接

题意:给你一个图 (单向边), 要求你找到一棵树, 根结点是1, 可以从根结点到达其他任意一个结点, 且总权值最小。

思路: 模板题。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
typedef long double ld;
const double eps = 1e-6, PI = 3.1415926535897932384626433832795;
const int mod = 1000000000 + 7;
const double INF = (double)10000000000000;
// & 0x7FFFFFFF
const int seed = 131;
const ll INF64 = ll(1e18);
const int maxn = 100 + 10;
int T,n,m,u,v;
  double w[maxn][maxn]; // 边权
  int vis[maxn];     // 访问标记,仅用来判断无解
  double ans;           // 计算答案
  int removed[maxn]; // 每个点是否被删除
  int cid[maxn];     // 所在圈编号
  int pre[maxn];     // 最小入边的起点
  double iw[maxn];      // 最小入边的权值
  int max_cid;       // 最大圈编号
struct node {
    double x, y;
    node(double x=0.0, double y=0.0):x(x), y(y) {}
}a[maxn];

void init() {
    for(int i=0;i<n;i++) {
        for(int j=0;j<n;j++) {
            w[i][j] = INF;
        }
    }
}
  // 从s出发能到达多少个结点
int dfs(int s) {
    vis[s] = 1;
    int ans = 1;
    for(int i = 0; i < n; i++)
      if(!vis[i] && w[s][i] < INF && fabs(w[s][i]-INF) > eps) ans += dfs(i);
    return ans;
}
  // 从u出发沿着pre指针找圈
 bool cycle(int u) {
    max_cid++;
    int v = u;
    while(cid[v] != max_cid) { cid[v] = max_cid; v = pre[v]; }
    return v == u;
}
  // 计算u的最小入弧,入弧起点不得在圈c中
void update(int u) {
    iw[u] = INF;
    for(int i = 0; i < n; i++)
      if(!removed[i] && (w[i][u] < iw[u] && fabs(w[i][u]-iw[u]) > eps)) {
        iw[u] = w[i][u];
        pre[u] = i;
      }
}
  // 根结点为s,如果失败则返回false
bool solve(int s) {
    memset(vis, 0, sizeof(vis));
    if(dfs(s) != n) return false;

    memset(removed, 0, sizeof(removed));
    memset(cid, 0, sizeof(cid));
    for(int u = 0; u < n; u++) update(u);
    pre[s] = s; iw[s] = 0; // 根结点特殊处理
    ans = 0;  max_cid = 0;
    for(;;) {
      bool have_cycle = false;
      for(int u = 0; u < n; u++) if(u != s && !removed[u] && cycle(u)){
        have_cycle = true;
        // 以下代码缩圈,圈上除了u之外的结点均删除
        int v = u;
        do {
          if(v != u) removed[v] = 1;
          ans += iw[v];
          // 对于圈外点i,把边i->v改成i->u(并调整权值);v->i改为u->i
          // 注意圈上可能还有一个v'使得i->v'或者v'->i存在,因此只保留权值最小的i->u和u->i
          for(int i = 0; i < n; i++) if(cid[i] != cid[u] && !removed[i]) {
            if(w[i][v] < INF && fabs(w[i][v]-INF) > eps) w[i][u] = min(w[i][u], w[i][v]-iw[v]);
            w[u][i] = min(w[u][i], w[v][i]);
            if(pre[i] == v) pre[i] = u;
          }
          v = pre[v];
        } while(v != u);
        update(u);
        break;
      }
      if(!have_cycle) break;
    }
    for(int i = 0; i < n; i++)
      if(!removed[i]) ans += iw[i];
    return true;
}

int main() {
//    ios::sync_with_stdio(false);
    while(~scanf("%d%d",&n,&m)) {
        for(int i=1;i<=n;i++) {
            scanf("%lf%lf",&a[i].x,&a[i].y);
        }
        init();
        for(int i=0;i<m;i++) {
            scanf("%d%d",&u,&v);
            double cost = sqrt((a[u].x-a[v].x)*(a[u].x-a[v].x) + (a[u].y-a[v].y)*(a[u].y-a[v].y));
            if(u == v) continue;
            w[u-1][v-1] = min(w[u-1][v-1], cost);
        }
        if(solve(0)) printf("%.2f\n",ans);
        else printf("poor snoopy\n");
    }
    return 0;
}


你可能感兴趣的:(poj,ACM-ICPC,最小树形图)