HDU 2121 Ice_Cream's World II (最小树形图)

(还是比较灵异,我代码重写了一遍,就对了,之前不知道哪里错了,一直wa)

题目分析出来就是无固定跟的最小树形图。

无固定根的话,那就给定一个人工的根!

然后复习一下最小树形图的建图流程:

1、首先,除了根节点以外,给每个节点找到一个最小入边;然后检查除根节点以外其他节点,如果有孤立的节点的话,说明无解!

2、找环,因为只要到达环上的任意一点,那么就可以到达这个环上的其他的所有的点,所以说,找一个对于一个环来讲,入边最小的边作为环的入边即可,因此我们可以把环看做是一个点,将环上的点的编号统一成一个,就是这个新的节点的编号,这个过程就是缩点!

3、图发生了改变,那么就要改变原图,改变分为两步,第一步是改节点的编号,是缩点的点在第2步就改完了,这里就是改不在环上的点的标号;第二步是改变边的权值,可以这样想,一定是要从环上删除一个边,然后再加入一条边,因为每次都选最小入边,所以删除的这条边是这个节点的入边的最小,那么无论再加入哪一个节点,都是增加权值的,那么成家的权值就是加边减去原来的最小入边,那么就可以直接将这个点的所有入边改为增加的权值,那么就可以直接再执行1和2,知道没有环存在,图还连通!

4、说一下这个人工节点,当人工只和一个原图中的点相连的时候,原图才连通!

那么说一下这道题:

题目:给定一个有向图,让你求这个图的最小树形图,并输出最小树形图的值和根节点编号。

分析:首先,要想输出根节点,那就看看这个边的原点是不是虚拟的根节点,如果是的话,那么这个点就是根节点,如果这个根节点在一个环上的话,那么这个根节点可以是这个环上的任意一个点;然后,就是求最小树形图,根节点到每个点的权值是所有边的权值和加1,总之比所有边的权值大,如果得到的ans它很大,已经超了()Sum+最小是树形图的总权值)的话,那就说明一共有两个点和虚拟根相连才能保证这个最小树形图是连通的话,说明原图没有解!

细节见代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int M = 10010;
const int N = 1010;
const int INF = 100000001;
struct Node {
    int u, v, w;
}e[M];

int n, m;
int pre[N], vis[N], id[N], in[N];
int idx, pos;
int dirMST( int root, int V, int E ) {
   int ret = 0;
   while ( 1 ) {
       for ( int i = 0; i < V; ++i ) in[i] = INF;
       for ( int i = 0; i < E; ++i ) {
           int u = e[i].u, v = e[i].v, w = e[i].w;
           if ( u != v && w < in[v] ) {
               in[v] = w, pre[v] = u;
               if ( u == root ) pos = i;
           }
       }
       in[root] = 0;
       for ( int i = 0; i < V; ++i ) 
           if ( in[i] == INF ) return -1;
       memset( vis, -1, sizeof(vis) );
       memset( id, -1, sizeof(id) );
       idx = 0;
       for ( int i = 0; i < V; ++i ) {
           ret += in[i];
           int v = i;
           while ( vis[v] != i && id[v] == -1 && v != root ) {
               vis[v] = i;
               v = pre[v];
           }
           if ( v != root && id[v] == -1 ) {
               for ( int u = pre[v]; u != v; u = pre[u] ) id[u] = idx;
               id[v] = idx++;
           }
       }
       if ( idx == 0 ) break;
       for ( int i = 0; i < V; ++i ) if ( id[i] == -1 ) id[i] = idx++;

       for ( int i = 0; i < E; ++i ) {
           int u = e[i].u, v = e[i].v, w = e[i].w;
           e[i].u = id[u];
           e[i].v = id[v];
           if ( id[u] != id[v] ) e[i].w -= in[v];
       }
       V = idx;
       root = id[root];
   }
   return ret;
}



int main()
{
    while ( scanf("%d%d", &n, &m) != EOF ) {
        int sum = 0, i;
        for ( i = 0; i < m; ++i ) {
            scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w );
            e[i].u++, e[i].v++;
            sum += e[i].w;
        }
        sum++;
        for ( i = m; i < n+m; ++i ) 
            e[i].u = 0, e[i].v = i - m + 1, e[i].w = sum;
        int ans = dirMST( 0, n+1, n+m );
       //cout << ans << endl;
        if ( ans == -1 || ans - sum >= sum ) printf("impossible\n\n");
        else printf("%d %d\n\n", ans-sum, pos - m );
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值