HDU 3790 最短路径问题

时间:2022-12-15 04:34:04
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 12921    Accepted Submission(s): 3940


Problem Description
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
 

Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
 

Output
输出 一行有两个数, 最短距离及其花费。
 

Sample Input
 
 
3 2 1 2 5 6 2 3 4 5 1 3 0 0
 

Sample Output
 
 
9 11
 

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 12921    Accepted Submission(s): 3940


Problem Description
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
 

Input
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
 

Output
输出 一行有两个数, 最短距离及其花费。
 

Sample Input
 
 
3 2 1 2 5 6 2 3 4 5 1 3 0 0
 

Sample Output
 
 
9 11
 
题意就是输出路程最短的,路程最短的有多条则输出最省钱的。所以只需要在更新路程d[i]的时候顺带更新花费fare[i]
。然后当
d[v[i]]==d[u]+w[i]的时候如果满足
fare[v[i]]>fare[u]+f[i], 就更新花费即可。
中间有一段是用SPFA算的。两种均可。
其实1个最小,2个最小和3个最小都是一样的,也行以后还会多一个其他花费,比如其他等等。同理可得。

<span style="color:#330033;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <stack>
using namespace std;
typedef pair<int ,int>pii;
const int maxn = 1030;
const int maxm = 200010;
const int inf = 0x3f3f3f3f;
int v[maxm],next[maxm],w[maxm];
int first[maxn],d[maxn],inq[maxn],e,i,f[maxm],fare[maxn];
 //queue<int> q;
void init(){
    e = 0;
    memset(first,-1,sizeof(first));
}

void add_edge(int a,int b,int c,int g){
    v[e] = b;next[e] = first[a];w[e] = c;f[e]=g;first[a] = e++;</span>
<span style="color:#330033;">//要特地开一个数组用于记录花费,跟记录路程一样
}

/*void spfa(int src){

    memset(d,0x3f,sizeof(d));
    d[src] = 0,inq[src] = 1;
    fare[0]=0;
    q.push(src);
    while(!q.empty()){
        int u = q.front();
       q.pop();
        inq[u] = 0;
        for(int i = first[u];i != -1;i = next[i])
        {
            if(d[v[i]] > d[u]+w[i])
               {
                d[v[i]] = d[u]+w[i];
                  fare[v[i] ]=fare[ u ]+f[i];
                if(!inq[v[i]])  {q.push(v[i]);inq[v[i]] = 1;}
               }
            else
            if(d[v[i]] == d[u]+w[i]&&fare[v[i] ]>fare[ u ]+f[i])
               {
                d[v[i]] = d[u]+w[i];
                  fare[v[i] ]=fare[ u ]+f[i];
            if(!inq[v[i]])  {q.push(v[i]);inq[v[i]] = 1;}
               }
        }
    }
}

*/

void dijstra(int src )
{
    priority_queue<pii,vector<pii>,greater<pii> > q;
    while(!q.empty())
            q.pop();
    memset(d,-1,sizeof(d));
    d[src]=0;
    fare[src]=0;   //必须赋值,跟赋d[src]同一个道理
    q.push(make_pair(0,src));
    while(!q.empty())
    {
        while(!q.empty()&&q.top().first>d[q.top().second])
            q.pop();
        if(q.empty())
            break;
        int u=q.top().second;
        q.pop();
        for(i=first[u];i!=-1;i=next[i])
        {
            if(d[v[i]]==-1||d[v[i]]>d[u]+w[i])
            {
                d[v[i]]=d[u]+w[i];
                fare[v[i]]=fare[u]+f[i];
                q.push(make_pair(d[v[i]],v[i]));
            }
            else
            if(d[v[i]]==d[u]+w[i])//如果路程相等,取花费少的。
                 if(fare[v[i]]>fare[u]+f[i])
                    {
                        fare[v[i]]=fare[u]+f[i];
                        q.push(make_pair(d[v[i]],v[i]));
                    }
        }
    }

}
int main(){
    int n,m,s,t,a,b,c,g,st,ed;
    while(scanf("%d%d",&n,&m))
    {if(n==0&&m==0)
        break;
        init();

        for(int i = 0;i < m;i++){
            scanf("%d%d%d%d",&a,&b,&c,&g);
            add_edge(a,b,c,g);
            add_edge(b,a,c,g);
        }
        scanf("%d%d",&st,&ed);
        //spfa(st);
        dijstra(st);
            printf("%d %d\n",d[ed],fare[ed]);
    }
    return 0;
}</span>
程序第一次提交的时候竟然错了,找了半天发现是f[]开小了,开成了maxn.实际应该是maxm。因为它跟v数组都是共用e下标的,e是边数,所以应该是maxm。以后不管3721直接开maxm得了,如果超了再改小点。。免得出错。。
还有一点就是必须赋f[src]=0,不然也是错的,跟赋d[src]=0同一个道理。