Ural 1085|Meeting|Floyd|最短路径

本文介绍了一道程序设计问题:如何在电车票价上涨的情况下,为团队选择最优的集合地点,以实现最少的交通费用支出。通过使用Floyd算法解决多源最短路径问题,并考虑个人预算与月票情况。

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

http://acm.timus.ru/problem.aspx?space=1&num=1085

题目

K个好朋友要庆祝他们在程序设计大赛夺得第一名。但是电车票却涨价了,他们住在城市的不同地方,他们需要选择一个电车站作为集合地点,又希望坐电车花的钱尽量少。你需要设计一个程序帮助他们选择集合地点。
城市里面有M个电车运行路线(他们只会坐电车,因为太远了)。每条路线经过的站点都已知。对于每个人我们知道他有多少钱、他是否有月卡(也就是坐电车不花钱)。一张电车票要4卢布(一张票整条电车运行路线通行,也就是说路线的站点间通勤一次只要4卢布)。
你需要挑出一个电车站作为集合地点,满足每个人不会用超出他自己的零花钱(他们之间不会相互借钱),并使得他们的总花销最小。如果一个人要换乘一次,就要另外购买一张票票乘坐其他路线的电车。

输入

第一行两个整数 N,M(1N,M100) ,分别表示站点数和路线数。接下来M行每行第一个整数 L(2L100) 表示该条路线会经过多少个站点,同一行接下来跟着 L 个整数表示该条路线会经过哪些站点(站点标号1~N)。
接下来一行一个整数K(1K100),表示人数。接下来 K 行,每行描述一个人,第一个整数表示这个人有多少钱,第二个整数表示这个人初始位置是哪个站点,第三个整数只会为0或1,1表示这个人有月卡,0表示没有。一个人不会有超过1000卢比。

输出

输出一行,如果存在一个站点使得每个人自己的钱足够买电车票到选定的集合地点,输出2个整数分别为集合地点和最小的总花费(如果有多个集合地点可选,随便输出一个)。
如果不存在输出一行一个数0。

样例输入

4 3
2 1 2
2 2 3
2 3 4
3
27 1 0
15 4 0
45 4 0

样例输出

4 12

题解

显然本题是多源最短路径,上Floyd。
由于一条路线上的所有站点间到一次都是4卢布,因此我们在初始化邻接矩阵的时候相当于构造了一个完全子图。然后跑一边floyd。
枚举每个站点设为集合地点,那么枚举每个人判断一下就好了。
打比赛的时候有人INF加爆了。。这个问题要注意。

#include <cstdio>
#include <cstring>
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int inf = 0x3f3f3f3f;
const int N = 110;
int ruble[N], from[N], hasTicket[N];
int stop[N], f[N][N];
int n, k;

void floyd() {
    int i, j, k;
    FOR(k,1,n) FOR(i,1,n) FOR(j,1,n)
        if (f[i][k] != inf && f[k][j] != inf &&
            f[i][j] > f[i][k] + f[k][j])
            f[i][j] = f[i][k] + f[k][j];
}


int main() {
    int m, l, i, j, mi = inf, p, c;

    scanf("%d %d", &n, &m);
    memset(f, 0x3f, sizeof f);
    FOR(i,1,n) f[i][i] = 0;

    while (m--) {
        scanf("%d", &l);
        FOR(i,1,l) scanf("%d", &stop[i]);
        FOR(i,1,l) FOR(j,i+1,l)
            f[stop[i]][stop[j]] = f[stop[j]][stop[i]] = 4;
    }

    scanf("%d", &k);
    FOR(i,1,k) scanf("%d%d%d", &ruble[i], &from[i], &hasTicket[i]);

    floyd();

    FOR(i,1,n) {
        c = 0;
        FOR(j,1,k) {
            if (f[i][from[j]] == inf || !hasTicket[j] && f[i][from[j]] > ruble[j]) {
                c = inf;
                break;
            }
            if (!hasTicket[j])
                c += f[i][from[j]];
        }
        if (mi > c) {
            mi = c;
            p = i;
        }
    }

    if (mi == inf) puts("0");
    else printf("%d %d\n", p, mi);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值