dijkstra算法 打印两个点的所有最短路径 pta L2-001 紧急救援

L2-001 紧急救援

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例:

2 60
0 1 3

代码长度限制         16 KB

时间限制         200 ms

内存限制         64 MB

栈限制         8192 KB

也是一道经典pta的l2阶段的题了,这道题关键是求出所有的最短路径的条数,本人比较菜刚开始没有想出来怎么实现,试着用dfs+剪枝看看呢不能蹭过,结果发现最后一个测试点死活过不去,又想到了用并查集记录一下哪个点能到达终点,但是只快了1ms,最后一个测试点死活过不去。

附上代码:

#include <bits/stdc++.h>
using namespace std;
struct edge{
    int from,to;
    long long int w;
    edge(int a,int b,long long int c) {from=a;to=b;w=c;}
}; 
vector <edge> e[505];
long long int ab_w[505][505];
vector <int> path;
vector <int> ans;
long long int path_len=1e15;
long long int path_sum=0;
int path_cnt=0;
bool book[505];
int jiu[505];
int fu[505];
bool is_dao[505];
int n,m,s,d;
int find_fu(int x)
{
	if( fu[x]==x ) return x;
	return fu[x]=find_fu(fu[x]);
} 
void dfs(int now)
{
	if(now==d)
	{
		long long int len=0;
		for(int i=0;i<path.size();i++)
		{
			//cout<<path[i]<<' ';
			if(i<path.size()-1) len+=ab_w[ path[i] ][ path[i+1] ];
		}
		//cout<<'\n';
		if(len<path_len)
		{
			path_cnt=1;
			path_len=len;
			long long int sum=0;
			for(int i=0;i<path.size();i++)
			{
				sum+=jiu[ path[i] ];
			}
			path_sum=sum;
			ans.clear();
			for(int i=0;i<path.size();i++)
			{
				ans.push_back(path[i]);
			}
			return;
		}
		if(len==path_len)
		{
			path_cnt++;
			long long int sum=0;
			for(int i=0;i<path.size();i++)
			{
				sum+=jiu[ path[i] ];
			}
			if(sum>path_sum)
			{
				path_sum=sum;
				ans.clear();
				for(int i=0;i<path.size();i++)
				{
					ans.push_back(path[i]);
				}
			}
			return;
		}
		return;
	}
	if(is_dao[now]==0) return;
	for(int i=0;i<e[now].size();i++)
	{
		if(book[ e[now][i].to ]==0)
		{
			path.push_back(e[now][i].to);
			book[ e[now][i].to ]=1;
			dfs(e[now][i].to);
			path.pop_back();
			book[ e[now][i].to ]=0;
		}
	}
	
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 
    cin>>n>>m>>s>>d;
    for(int i=0;i<n;i++){e[i].clear();book[i]=0;fu[i]=i;}
    for(int i=0;i<n;i++)
    {
        cin>>jiu[i];
    }
    for(int i=0;i<m;i++)
    {
        int a,b;long long int c;cin>>a>>b>>c;
        e[a].push_back( edge(a,b,c) );
        e[b].push_back( edge(b,a,c) );
        ab_w[a][b]=c;
        ab_w[b][a]=c;
        if( find_fu(a)!=find_fu(b) ) fu[ find_fu(a) ]=find_fu(b);
    }
    for(int i=0;i<n;i++)
    {
    	if( find_fu(i) == find_fu(d) ) is_dao[i]=1;
    	else is_dao[i]=0;
	}
    path.push_back(s);
	book[s]=1;
    dfs(s);
    //cout<<'\n'; 
    cout<<path_cnt<<' '<<path_sum<<'\n';
    for(int i=0;i<ans.size();i++)
    {
    	cout<<ans[i];
    	if(i!=ans.size()-1) cout<<' ';
	}
    return 0;
}
想到dij算法中,关键代码有一个比较 下一个节点到起点的最短距离 是不是小于 目前节点到起点的距离+目前节点到下一个几点的距离( dis[nex_id] > dis[now.id] + nex_w
我们都知道,如果要打印路径,要用一个pre[ ]数组去存储每个结点的上一个节点,那么如果当(dis[nex_id] == dis[now.id] + nex_w)的时候,是不是可以存多个呢,用vector <int> pre[n]存储更加方便,这样就知道i节点的pre[i]有几个父节点。并且打印路径的时候要用到dfs的方法。
果然ac了,看来dfs即使剪枝也是太慢了。

附上代码:

#include <bits/stdc++.h>
using namespace std;
struct edge{
    int from,to;
    long long int w;
    edge(int a,int b,long long int c) {from=a;to=b;w=c;}
};
struct node{
    int id;
    long long int i_to_d_dis;
    node(int a,long long int b){id=a;i_to_d_dis=b;}
    bool operator < (const node a) const
    {
        return i_to_d_dis>a.i_to_d_dis;
    }
};
vector < edge > e[505];
long long int dis[505];
bool book[505];
int jiu[505];
vector <int> pre[505];
bool pre_book[505]; 
vector <int> path;
long long int ans_sum=0;
vector <int> ans; 
int cnt=0;
int n,m,s,d;
void find_path(int now)
{
    if(now==s) 
    {
    	cnt++;
    	long long int sum=0;
    	for(int i=path.size()-1;i>=0;i--) 
    	{
    		//cout<<path[i]<<' ';
    		sum+=jiu[ path[i] ];
		}
		//cout<<"   sum="<<sum<<'\n';
		if(ans_sum<sum)
		{
			ans_sum=sum;
			/*ans.clear();
			for(int i=0;i<path.size();i++) ans.push_back(path[i]);*/
			ans=path; 
		}
    	return;
	}
	for(int i=0;i<pre[now].size();i++)
	{
		if(pre_book[pre[now][i]]==0)
		{
			pre_book[pre[now][i]]=1;
			path.push_back(pre[now][i]);
			find_path(pre[now][i]);
			path.pop_back();
			pre_book[pre[now][i]]=0;
		}
		
	}
}
void dij()
{
    dis[s]=0;
    priority_queue <node> dui; 
    dui.push( node(s,dis[s]) );
    while(dui.size()>0)
    {
        node now=dui.top();
        dui.pop();
        if(book[now.id]==1) continue;
        book[now.id]=1;
        for(int i=0;i<e[now.id].size();i++)
        {
            int nex_id=e[now.id][i].to;
            long long int nex_w=e[now.id][i].w;
            if(dis[nex_id] > dis[now.id] + nex_w )
            {
                dis[nex_id] = dis[now.id] + nex_w;
                dui.push( node(nex_id,dis[nex_id]) );
                pre[nex_id].clear();
                pre[nex_id].push_back(now.id);
            }
  			else if(dis[nex_id] == dis[now.id] + nex_w )
  			{
  				dui.push( node(nex_id,dis[nex_id]) );
  				pre[nex_id].push_back(now.id);
			}
        } 
    }
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m>>s>>d;
    for(int i=0;i<n;i++){e[i].clear();book[i]=0;dis[i]=1e15;pre[i].clear();pre_book[i]=0;}
    for(int i=0;i<n;i++)
    {
        cin>>jiu[i];
    }
    for(int i=0;i<m;i++)
    {
        int a,b;long long int c;cin>>a>>b>>c;
        e[a].push_back( edge(a,b,c) );
        e[b].push_back( edge(b,a,c) ); 
    }
    dij();
    //
    path.push_back(d);
    pre_book[d]=1;
    find_path(d);
    //
    /*for(int j=0;j<n;j++)
	{
		cout<<"节点"<<j<<"的前驱点为:"; 
		for(int i=0;i<pre[j].size();i++) cout<<pre[j][i]<<'.';
		cout<<'\n';
	} */
	cout<<cnt<<' '<<ans_sum<<'\n';
    for(int i=ans.size()-1;i>=0;i--)
    {
    	cout<<ans[i];
    	if(i>0) cout<<' ';
	}
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值