hdu1385

点击打开链接

货物从一个city到另一个city,中间有收费,经过city时要收费,货物从a到b,花费最少

Floyd:(要是先给出图,然后询问图中2点的cost,而对图没有改变,感觉这个好用点)

注意纪录路径;

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
#include <vector>
#include <stack>
using namespace std;
const int M=150;
int path[M][M],mp[150][150],vis[150],cost[150],pos[M],pre[M];
int n;
const int inf=100000000;
void Floyd()
{
    int i,j,k;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        path[i][j]=j;///从i到j默认为j,这样,自己到自己也合法了
    for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
    for(j=1;j<=n;j++){
        int temp=mp[i][k]+mp[k][j]+pos[k];
        if(temp<mp[i][j])
        {
            mp[i][j]=temp;
            path[i][j]=path[i][k];
        }
        if(temp==mp[i][j]){///字典序最小,这要学习下
            if(path[i][j]>path[i][k])
                path[i][j]=path[i][k];
        }
    }
}
int main()
{
    int a,b,x,i,j,k;
    while(~scanf("%d",&n),n)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
        {
            scanf("%d",&x);
            if(x!=-1)
                mp[i][j]=x;
            else mp[i][j]=inf;
        }
        for(i=1;i<=n;i++)
            scanf("%d",&pos[i]);
        Floyd();
        while(scanf("%d%d",&a,&b),a!=-1&&b!=-1)
        {
            printf("From %d to %d :\n",a,b);
            printf("Path: %d",a);
            int t=a;
            while(t!=b)
            {
                printf("-->%d",path[t][b]);
                t=path[t][b];
            }
            printf("\n");
            printf("Total cost : %d\n\n",mp[a][b]);

        }
    }
    return 0;
}

dijkstra:

这样纪录路径第一次见:当当前点的前驱已经存在时,再改变他的前驱是要考虑字典序,假设a,b是当前u的前驱,可以从u开始往前寻找到起点,然后再从起点开始,要是某一时刻i>j,则返回后面的路径,也就是选择后面的前驱

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <set>
#include <vector>
#include <stack>
using namespace std;
const int M=150;
int path[M],mp[150][150],vis[150],cost[150],pos[M],pre[M];
int n;
const int inf=100000000;
int zid(int s,int a,int b,int cur)//字典顺序
{
    int p1[M],p2[M],i=2,j=2;
    p1[0]=cur;
    p2[0]=cur;//当前位置
    p1[1]=a;
    p2[1]=b;
    while(a!=s)//沿路径返回存入数组
    {
       p1[i++]=pre[a];
       a=pre[a];
    }
    while(b!=s)
    {
        p2[j++]=pre[b];
       b=pre[b];
    }
    while(1)//从头开始比较,返回字典序小的。
    {
        i--,j--;
        if(i<0) return p1[1];
        else if(j<0) return p2[1];
        if(p1[i]>p2[j]) return p2[1];
        else if(p1[i]<p2[j]) return p1[1];
    }
}
void dijkstra(int st,int ed)
{
    int i,j,k;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)
        cost[i]=inf;
    memset(pre,-1,sizeof(pre));
    cost[st]=0;
    while(true)
    {
        int v=-1;
        for(int u=1;u<=n;u++){
            if(!vis[u]&&(v==-1||cost[u]<cost[v]))
                v=u;
        }
        if(v==-1)
            break;
        vis[v]=1;

        for(int u=1;u<=n;u++){
            if(!vis[u]&&cost[u]>cost[v]+mp[v][u]+pos[u]){
                cost[u]=cost[v]+mp[v][u]+pos[u];
                pre[u]=v;
            }
            else if(!vis[u]&&cost[u]==cost[v]+mp[v][u]+pos[u]){
                if(cost[u]<inf&&pre[u]!=-1)
                    pre[u]=zid(st,pre[u],v,u);
            }
        }
    }
//for(i=1;i<=n;i++)cout<<pre[i]<<" ";cout<<endl;
}
int main()
{
    int a,b,x,i,j,k;
    while(~scanf("%d",&n),n)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
        {
            scanf("%d",&x);
            if(x!=-1)
                mp[i][j]=x;
            else mp[i][j]=inf;
        }
        for(i=1;i<=n;i++)
            scanf("%d",&pos[i]);

        while(scanf("%d%d",&a,&b),(a!=-1||b!=-1))
        {
            printf("From %d to %d :\n",a,b);
            if(a==b)///由于初始化的时候都是-1,故要是有自己到自己的点输出错误,因此特判下
           {
               printf("Path: %d\n",a);
               printf("Total cost : 0\n\n");
               continue;
           }
            dijkstra(a,b);
            stack<int>st;
            st.push(b);
            for(i=b;i!=a;i=pre[i]){
                st.push(pre[i]);
            }

            printf("Path: %d",a);
            st.pop();
            while(!st.empty()){
                printf("-->%d",st.top());
                st.pop();
            }
            printf("\n");
            printf("Total cost : %d\n\n",cost[b]-pos[b]);

        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值