G - Ice_cream’s world II (朱刘算法+不定根)

本文介绍了一个基于图论的问题,即如何在一个由城市和道路组成的网络中找到一个最优的位置作为首都,使得从首都出发可以通过最经济的方式连接到网络中的每一个城市。文章通过一个具体的算法实现来解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 10005;
struct nede
{
    int u;
    int v;
    int cost;
} Edge[maxn*maxn+10];
int pre[maxn], visit[maxn], id[maxn];
int in[maxn];
int edge_point, edge_w;
int zhuliu(int root, int n, int m)
{
    int res = 0;
    int v;
    while(1)
    {
        for(int i=0; i<n; i++)//到达当前点的最小权值,先初始化
            in[i] = INF;
        for(int i=0; i<m; i++)//遍历每一个边
        {
            if(Edge[i].u!=Edge[i].v&&Edge[i].cost<in[Edge[i].v])//如果是一个边并且权值小于到达当前点的权值,更新
            {
                pre[Edge[i].v] = Edge[i].u;//pre记录前一个点,也就是从哪一点到达当前点
                in[Edge[i].v] = Edge[i].cost;//更新权值
                if(Edge[i].u==root)//更新超级点
                    edge_point = i;
            }
        }
        for(int i=0; i<n; i++)//遍历每一个节点
        {
            if(i!=root&&in[i]==INF)//如果当前点不是根节点,但入度值最小为INF,也就是没有任何边指向此点,那么就不存在最小树形图
                return -1;//返回-1
        }
        memset(id, -1, sizeof(id));//记录环的标号
        memset(visit, -1, sizeof(visit));//是否访问过
        int tn = 0;//环数
        in[root] = 0;//根节点清零
        for(int i=0; i<n; i++)//遍历每个节点,求最短弧的集合
        {
            res += in[i];//加入权值
            int v = i;
            while(visit[v]!=i&&id[v]==-1&&v!=root)//如果当前点没有被访问过,并且不是根节点,并且不在任何一个环上
            {
                visit[v] = i;
                v = pre[v];
            }
            if(id[v]==-1&&v!=root)
            {
                for(int u=pre[v]; u!=v; u=pre[u])
                {
                    id[u] = tn;
                }
                id[v] = tn++;
            }
        }
        if(tn==0)//没有环出现,则找到了最小树形图
            break;//跳出
        for(int i=0; i<n; i++)//收缩有向环
        {
            if(id[i]==-1)
            {
                id[i] = tn++;
            }
        }
        for(int i=0; i<m;i++)
        {
            v = Edge[i].v;
//            int u = Edge[i].u;
            Edge[i].u = id[Edge[i].u];
            Edge[i].v = id[Edge[i].v];
            if(Edge[i].u!=Edge[i].v)
                Edge[i].cost -= in[v];
//            else
//                swap(Edge[i], Edge[--m]);
        }
        n = tn;
        root = id[root];
    }
    return res;
}
int main()
{
    int n, m;
    while(~scanf("%d %d", &n, &m))
    {
        edge_w = 0;//计算权值
        for(int i=0; i<m; i++)
        {
            int u, v, cost;
            scanf("%d %d %d", &u, &v, &cost);
            u++;//从1开始
            v++;
            Edge[i].u = u;
            Edge[i].v = v;
            if(u!=v)
            Edge[i].cost = cost;
            else
            Edge[i].cost = INF;
            edge_w += cost;
        }
        edge_w++;//权值+1,是为了成为最大权值的一条边
        for(int i=0; i<n; i++)//创建一个虚拟点,然后到每个点的权值为最大值
        {
            Edge[i+m].u = 0;
            Edge[i+m].v = i+1;
            Edge[i+m].cost = edge_w;
        }
        int res = zhuliu(0, n+1, m+n);//找最小树形图
        if(res==-1||res-edge_w>=edge_w)//如果权值-1或者太大了,则不可能
            printf("impossible\n\n");
        else
            printf("%d %d\n\n", res-edge_w, edge_point-m);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值