RoboCom 2021 编程技能赛决赛 7-4 猛犸不上 Ban

1 篇文章 0 订阅

7-4 猛犸不上 Ban

赛题

分数 30  作者 DAI, Longao  单位 杭州百腾教育科技有限公司

mammoth-gd01e31f27_640.png

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

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

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

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

输入格式:

输入第一行是四个正整数 N,M,S,T (2≤N≤500,1≤M≤10^5),表示有 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!

代码长度限制

16 KB

时间限制

800 ms

内存限制

64 MB

栈限制

8192 KB

思路

1.寻找最短路径问题。

2.第一种方式回到S城市,可以分解成:到了某相邻城市,然后再回来。

3.第二种方式,直接到T城市,可以看作S为原点,到T的最短路径。

4.第一种方式的最小值,应该为S和该相邻城市的直线距离与不走该直线,S和该城市的最短距离之和。

5.两种方式都可以使用dijkstra算法。

AC

//7-4 猛犸不上 Ban dijkstra算法

#include <bits/stdc++.h>

using namespace std;
int N,M,S,T;// N 个城市,M 条道路,从S城市出发,可以选择到达T城市
const int SIZE = 505;
#define ll long long
int g[SIZE][SIZE];//存储两个城市间的权重、人数
int x,y,v;
int d[SIZE];//从S出发,最短距离
bool dv[SIZE];//从S出发,最短距离,标记是否走过
bool dv2[SIZE][SIZE];//从S到T直线距离,标记是否走过
int pre[SIZE];//S节点的前置节点
const int big = 0x3f3f3f;

//所有节点距离原始节点的最短距离
void dijkstra1() {
	int c=-1;//当前选中节点
	while(true) {
		c=-1;
		//选出当前未访问的最近节点,第一次时d[S]为0,自身最近
		for(int i=1; i<=N; i++) {
			if(!dv[i]&&(c==-1||d[i]<d[c])) {
				c=i;
			}
		}
		if(c==-1) {
			break;//均已经访问完
		}
		dv[c]=true;
		for(int i=1; i<=N; i++) {
			if(d[i]>d[c]+g[c][i]) {
				//若和当前节点相邻,会被标记处,且标记处最短的
				d[i]=d[c]+g[c][i];
				pre[i]=c;
			}
		}
	}
}

//所有节点距离原始节点的不走最短距离的次最短距离
void dijkstra2(int xx) {
	int c=-1;//当前选中节点
	while(true) {
		c=-1;
		//选出当前未访问的最近节点,第一次时d[S]为0,自身最近
		for(int i=1; i<=N; i++) {
			if(!dv[i]&&(c==-1||d[c]>d[i])) {
				c=i;
			}
		}
		if(c==-1) {
			break;//均已经访问完
		}
		dv[c]=true;
		for(int i=1; i<=N; i++) {
			//如果当前节点到目标节点正好是不能走的那条路 
			if(dv2[c][i]) continue;
			if(d[i]>d[c]+g[c][i]) {
				//若和当前节点相邻,会被标记处,且标记处最短的
				d[i]=d[c]+g[c][i];
			}
		}
	}
}

int main() {
	cin>>N>>M>>S>>T;
	//未告诉的说明不通,无限大
	memset(g,big,sizeof g);
	for(int i=1; i<=M; i++) {
		cin>>x>>y>>v;
		g[x][y]=g[y][x]=v;
	}
	memset(d,big,sizeof d);
	memset(dv,false,sizeof dv);
	//目标节点为初始节点
	d[S]=0;
	dijkstra1();
	int min1=d[T];

	int min2=big;
	//已经遍历完S到T的距离
	//下面判断S途径一个绕回来的距离
	for(int i=1; i<=N; i++) {
		//所有S的一级子节点
		if(pre[i]==S) {
			memset(d,big,sizeof d);
			memset(dv2,false,sizeof dv2);
			memset(dv,false,sizeof dv);
			dv2[S][i]=dv2[i][S]=true;
			//目标节点为初始节点
			d[i]=0;
			dijkstra2(S);
			min2=min(d[S]+g[i][S],min2);
		}
	}

	cout<< (min2>=big? -1 : min2)<<" "<<(min1>=big? -1 :min1)<<endl;
	cout<<(min2<min1?"Win!":"Lose!")<<endl;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值