L2-2 超级玛丽
假定有n个城堡,编号为1至n,有的城堡之间有道路直接相连,有的城堡之间没有道路直接相连。马里奥现在准备从一个城堡出发前往另一个城堡,它有一个魔法棒,可以瞬时通过一条道路,即以0时间通过这条道路,但魔法棒最多只能用一次。马里奥想以最短的时间到达目的地,请编写程序为马里奥选定一条路线以及在什么地方使用魔法棒。假定所有道路为双向,保证从起点肯定能到达目的地。
输入格式:
输入第一行为4个整数n、s、t、m,分别表示城堡数(编号为1至n,n不超过10000),马里奥所在的起点s和想去的终点t,城堡间的道路数目。接下来m行,每行为3个正整数a、b、c,表示城堡a和城堡b之间有一条道路直接相连,通过该道路需要c分钟。
输出格式:
输出为空格间隔的2个整数,第1个整数为马里奥从起点到目的地所需的最短时间;第2个整数表示使用魔法棒的地点,即城堡编号,若有多个可能的地点,则输出编号最小者。
输入样例:
4 1 4 4
1 2 9
2 4 1
1 3 3
3 4 5
输出样例:
1 1
法一:dfs,记录路径上的总和,最大边和最大边对应编号
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int N=2e5+10;
int n,m,S,T,ans=1e8;
int vis[N],h[N],v[N],to[N],ne[N],idx;
int ansno=1e8;
void add(int x,int y,int z) {
to[idx]=y,v[idx]=z,ne[idx]=h[x],h[x]=idx++;
to[idx]=x,v[idx]=z,ne[idx]=h[y],h[y]=idx++;
}
//s是当前访问点,sum是到这个点的路径和,maxx是最大边的值,no是最大边对应编号
void dfs(int s,int sum,int maxx,int no) {
//剪枝
if(sum-maxx>ans)
return;
if(s==T) {
//有更优情况,ans和编号都更新
if(ans>sum-maxx) {
ans=sum-maxx;
ansno=no;
} else//选择最小的编号更新
ansno=min(ansno,no);
return;
}
for(int i=h[s]; ~i; i=ne[i]) {
int u=to[i];
if(vis[u]) continue;
int c=v[i];
vis[u]=1;
if(maxx<c){
//当前边比之前的最大边大,传递这个边和这个点编号
dfs(u,sum+c,c,s);
}else if(maxx==c)
//边相等,传递最小的编号
dfs(u,sum+c,c,min(s,no));
else dfs(u,sum+c,maxx,no);//传递原来的
vis[u]=0;//回溯
}
}
int main() {
scanf("%d%d%d%d",&n,&S,&T,&m);
memset(h,-1,sizeof h);
for(int i=1; i<=m; i++) {
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
//从起点开始,那么默认起点已经访问了
vis[S]=1;
dfs(S,0,-1,1e8);
cout<<ans<<" "<<ansno;
return 0;
}
法二:两遍最短路,枚举边。
分析:对于任意一条要删除的边,设两端为u,v。那么现在的路径就是:**S到u的最短路径+v到T的最短路径。**那么我们可以很自然想到,跑两次最短路,分别是S到各点的距离和T到各点的距离。最后枚举m条边,得出最优解。
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n, m, s, t, inf=1e8, ans=1e8, pos;
struct {
int a,b;
}E[100005];
struct node{
int e;
int w;
node(int a,int b):e(a), w(b){}
};
vector<node> mp[10005];
int diss[10005], dist[10005];
//这里传入了数组,方便两次最短路 代码复用
void dijkstra(int dis[], int st){
fill(dis+1,dis+1+n, inf);
dis[st]=0;
typedef pair<int, int> P;
priority_queue<P, vector<P>, greater<P> > q;
q.push(P(0,st));
while(!q.empty()){
P p = q.top(); q.pop();
int v = p.second;
if(dis[v]<p.first) continue;
for(auto i:mp[v]){
if(dis[i.e]>dis[v]+i.w){
dis[i.e]=dis[v]+i.w;
q.push(P(dis[i.e], i.e));
}
}
}
}
int main(){
cin>>n>>s>>t>>m;
for(int i=0;i<m;i++){
int a,b,c;cin>>a>>b>>c;
E[i].a=a;E[i].b=b;
mp[a].push_back(node(b,c));
mp[b].push_back(node(a,c));
}
//两次最短路
dijkstra(diss,s);
dijkstra(dist,t);
for(int i=0;i<m;i++){
int a= E[i].a;
int b= E[i].b;
//比ans小,直接更新ans和编号
if(diss[a]+dist[b]<ans) ans=diss[a]+dist[b],pos=a;
//比ans一样,编号更新最小的
else if(diss[a]+dist[b]==ans&&pos>a) pos=a;
if(diss[b]+dist[a]<ans) ans=diss[a]+dist[b],pos=b;
else if(diss[b]+dist[a]==ans&&pos>b) pos=b;
}
printf("%d %d", ans,pos);
return 0;
}