[最短路]猛犸不上 Ban 2021RoboCom决赛D

48 篇文章 0 订阅

在一个名叫刀塔的国家里,有一只猛犸正在到处跑着,希望能够用它的长角抛物技能来撞飞别人。已知刀塔国有 N 座城市,城市之间由 M 条道路互相连接,为了拦住这头猛犸,每条道路上设置了 Vi​ 人的团队。

这只猛犸从 S 号城市出发,它可以选择:

  1. 在不重复地经过若干条道路后回到 S 号城市;
  2. 在不重复地经过若干条道路后到达 T 号城市。

猛犸经过一条道路后,就会把路上的人全部撞飞。作为一头爱喝雪碧的仁慈的猛犸,自然希望尽可能的少撞飞人。请你帮忙计算一下在最优的选择下,最少需要撞飞多少人才能够到达目标城市?

输入格式:

输入第一行是四个正整数 N,M,S,T (2≤N≤500,1≤M≤105),表示有 N 个城市,M 条道路,猛犸从 S 号城市出发,可以选择到达 T 号城市。

接下来的 M 行,每行三个正整数 Xi​,Yi​,Vi​ (0≤Vi​≤100),表示从 Xi​ 号城市到 Yi​ 号城市有一条道路,道路上有 Vi​ 人的团队。道路可双向通行,城市编号从 1 开始,两个城市之间最多只有一条道路,且没有一条道路连接相同的城市。

数据保证两种选择里至少有一种是可行的。

输出格式:

输出两行,第一行是两个数字,分别对应上面的两种选择分别最少需要撞飞多少人。如果无论撞飞多少人都无法满足选择要求,则输出 -1

第二行是一个句子,如果第一种(回到原点)的选择比较好,就输出 Win!,否则输出Lose!

输入样例:

5 6 1 5
1 2 1
2 3 2
3 4 3
4 1 5
3 5 4
4 5 1

输出样例:

在这里给出相应的输出。例如:

11 6
Lose!

题意: 有n个点m条边,起点s和终点t,现在猛犸有两种行动路径,一种是从s走到t,一种是从s出发转一圈再回到s,两种行动都不允许重复经过同一条边,问各自最短路是多少。

分析: 对于从s到t的最短路很简单,就是套一个dijkstra板子,对于从s出去再绕一圈回来的情况,其实就相当于从s的某个相邻节点i出发,并且删掉i到s的那条边的情况下从i走到s的最短路,再加上i到s的边权就是这一圈下来从i出发的最短路了,猛犸可能从s的各个相邻点出发,所以需要每个相邻点都处理一遍,也就是跑最多n-1次dijkstra,取一个最小值就是绕圈下的最短路了,时间复杂度为O(n*m*logn),如果不使用堆优化的dijkstra可能会更快。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#define inf 0x3f3f3f3f
#define pii pair<int, int>
using namespace std;

int n, m, s, t, mn;
vector<pii> g[505];
bool vis[505];
int dis[505];

void dijkstra(int s, int type){
	priority_queue<pii, vector<pii>, greater<pii> >q;
	dis[s] = 0;
	q.push(make_pair(dis[s], s));
	while(q.size()){
		int now = q.top().second;
		q.pop();
		if(vis[now]) continue;
		vis[now] = true;
		for(int i = 0; i < g[now].size(); i++){
			int to = g[now][i].second, w = g[now][i].first;
			if(type == 1 && now == s && to == ::s) continue;//禁用某条边 
			if(dis[to] > dis[now]+w){
				dis[to] = dis[now]+w;
				q.push(make_pair(dis[to], to));
			}
		}
	}
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> s >> t;
	for(int i = 1; i <= n; i++){
		dis[i] = inf;
		vis[i] = false; 
	} 
	for(int i = 1; i <= m; i++){
		int u, v, w;
		cin >> u >> v >> w;
		g[u].push_back(make_pair(w, v));
		g[v].push_back(make_pair(w, u));
	}
	dijkstra(s, 0);
	int temp = dis[t]; 
	int mn = inf;
	for(int i = 0; i < g[s].size(); i++){
		int to = g[s][i].second, w = g[s][i].first;
		for(int j = 1; j <= n; j++){
			vis[j] = false;
			dis[j] = inf;
		}
		dijkstra(to, 1);
		mn = min(mn, dis[s]+w);
	}
	if(mn != inf) cout << mn << " ";
	else cout << "-1 ";
	if(temp != inf) cout << temp;
	else cout << "-1";
	cout << endl;
	if(mn < temp) cout << "Win!";
	else cout << "Lose!";
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值