HDU 2121 Ice_cream’s world II (不定根的最小树形图)

Ice_cream’s world II

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7790    Accepted Submission(s): 2054

Problem Description

After awarded lands to ACMers, the queen want to choose a city be her capital. This is an important event in ice_cream world, and it also a very difficult problem, because the world have N cities and M roads, every road was directed. Wiskey is a chief engineer in ice_cream world. The queen asked Wiskey must find a suitable location to establish the capital, beautify the roads which let capital can visit each city and the project’s cost as less as better. If Wiskey can’t fulfill the queen’s require, he will be punishing.

Input

Every case have two integers N and M (N<=1000, M<=10000), the cities numbered 0…N-1, following M lines, each line contain three integers S, T and C, meaning from S to T have a road will cost C.

Output

If no location satisfy the queen’s require, you must be output “impossible”, otherwise, print the minimum cost in this project and suitable city’s number. May be exist many suitable cities, choose the minimum number city. After every case print one blank.

Sample Input

3 1

0 1 1

4 4

0 1 10

0 2 10

1 3 20

2 3 30

Sample Output

impossible

 

40 0

Author

Wiskey

Source

HDU 2007-10 Programming Contest_WarmUp

 不定根的最小树形图

如果分别以每个起点为根找最小树形图的话,会TLE

所以可以构造虚根

1、因为是不定根,假设一个虚拟根,分别与n个点相连,权值最穷大(其实只要大于原图中每个边的权值和即可),如果求出来的最小树形图选择了大于一条的虚边,那么原图肯定构不成最小树形图,所以就无解

2、由于每次遍历完后我们会对点重新编号,所以直接输出点的编号肯定是不行的,(ps:所以edge[real_rt].y不符合要求)所以我们只能从建立的虚根下手,我们可以发现我们新加的边是按顺序放进去的, 所以边的编号在一定程度上可以代表点的编号,并且对点重新编号不会影响边的信息. 所以我们记录边的信息.

#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <vector>
#include <cstring>
#include <string>
#include <cstdio>
#define Max 1003
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;

struct Edge{
    int x, y;
    int w;
}edge[10003];

int vis[Max];
int id[Max];//结点所属环编号
int in[Max];
int pre[Max];//in[]为最小入边权,pre[]为其对应的起点
int real_rt;

int zhuLiu(int root, int n, int m){//root结点、点数、边数
    int res = 0;//最小树形图总权值
    while(true){
        for(int i = 0; i < n; i++)//初始化为无穷大
            in[i] = inf;
 
        //寻找每个点的最小入边
        for(int i = 0; i < m; i++){//遍历每条边
            int x = edge[i].x;
            int y = edge[i].y;
            if(edge[i].w < in[y] && x != y){//更新最小入边
                pre[y] = x;//记录前驱
                in[y] = edge[i].w;//更新
                /*
                    由新加边确定根节点
                */
               //这条边的起点是 新加的虚根,记录一下这条边
                if(x == root) {
                    real_rt = i;
                    // cout << "***: " << i << endl;
                }
            }
        }
 
        //判断是否存在最小树形图
        for(int i = 0; i < n; i++){
            if(i == root)
                continue;
            if(in[i] == inf)//除根节点外的点存在孤立点
                return -1;
        }
 
        //寻找所有的环
        int cnt = 0;//记录环数
        in[root] = 0;
        memset(id, -1, sizeof(id));
        memset(vis, -1, sizeof(vis));
        for(int i = 0; i < n; i++){//标记每个环
            res += in[i];//记录权值
 
            int y = i;
            while(vis[y] != i && id[y] == -1&& y != root){//寻找图中有向环
                //三种情况会终止:找到出现同样标记的点、结点已属其他环、遍历到根
                vis[y] = i;//标记
                y = pre[y];//向上找
            }
 
            if(y != root && id[y] == -1){//没有遍历到根或没有找到结点属于其他环,说明找到有向环
                for(int x = pre[y]; x != y; x = pre[x])//标记结点x为第几个环
                    id[x] = cnt;//记录结点所属环号
                id[y] = cnt++;//记录结点所属环号并累加
            }
        }
        if(cnt == 0)//无环
                break;
        for(int i = 0; i < n; i++)//可能存在独立点
            if(id[i] == -1)//环数累加
                id[i] = cnt++;
 
        //建立新图,缩点重新标记
        for(int i = 0; i < m; i++){
            int x = edge[i].x;
            int y = edge[i].y;
            edge[i].x = id[x];
            edge[i].y = id[y];
 
            if(id[x] != id[y])//两点不在同一环内,更新边权值
                edge[i].w -= in[y];//x到y的距离为边权-in[y]
        }
 
        n = cnt;//以环数为下次操作的点数,继续上述操作,直到无环
        root = id[root];
    }
    return res;
}
int main(){
    int n, m;//n个点m条有向边
    while(scanf("%d %d", &n, &m)!=EOF)
    {
        int sum = 0;
        for(int i = 0; i < m; i++){//建图
            scanf("%d %d %lld", &edge[i].x, &edge[i].y, &edge[i].w);
        if(edge[i].x == edge[i].y)//除去自环,即点到自身距离为INF
            edge[i].w = inf;
        sum += edge[i].w;
        }
        sum++;
        for(int i = m; i < m + n; i++) {
            edge[i].x = n;
            edge[i].y = i - m;
            edge[i].w = sum;
        }

        int res=zhuLiu(n, n + 1, m + n);
        //明n结点不止和原图中的一个结点相连,所以原图不存在最小树形图
        if(res >= 2 * sum) printf("impossible\n\n");
        else {
            res -= sum;//删除虚边
            real_rt -= m; //首部的下标需减去原边数
            // edge[real_rt].y 不行
            printf("%lld %d\n\n", res, real_rt);
        }
    }

    return 0;
}
//!!!!所以我没加 EOF 就疯狂的 TLE ?????

//由于每次遍历完后我们会对点重新编号,所以直接输出点的编号肯定是不行的
//我们可以发现我们新加的边是按顺序放进去的, 所以边的编号在一定程度上可以代表点的编号,并且对点重新编号不会影响边的信息. 所以我们记录边的信息.

因为没有加 EOF就疯狂的 TLE,又debug了半个小时,我好菜,谁能99我……

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值