7-4 猛犸不上 Ban
赛题
分数 30 作者 DAI, Longao 单位 杭州百腾教育科技有限公司
在一个名叫刀塔的国家里,有一只猛犸正在到处跑着,希望能够用它的长角抛物技能来撞飞别人。已知刀塔国有 N 座城市,城市之间由 M 条道路互相连接,为了拦住这头猛犸,每条道路上设置了 Vi 人的团队。
这只猛犸从 S 号城市出发,它可以选择:
- 在不重复地经过若干条道路后回到 S 号城市;
- 在不重复地经过若干条道路后到达 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;
}