超级玛丽

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}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值