最短路练习

目录

最短路练习

poj 2387 Til the Cows Come Home

poj 2253 Frogger 

poj 1797 Heavy Transportation

poj 3268 Silver Cow Party

poj 1860 Currency Exchange

hdu 3790 最短路径问题

poj 2387 Til the Cows Come Home

题意:求起点到终点的最短距离。

bellman_ford算法:需要考虑重边。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int Max = 2e3+10;
const int INF = 0x3f3f3f3f;
typedef struct node{
    int to,from,ed;
}Node;
Node no[Max];
int V,E;
int d[Max];

void bellman_ford(int s)
{
   memset(d,0x3f,sizeof(d));
    d[s]=0;
    while(true)
    {
        bool update = false;
        for(int i=0;i<E;i++)
        {
            node e = no[i];
             if(d[e.from]!=INF&&d[e.to]>d[e.from]+e.ed){
                d[e.to]=d[e.from]+e.ed;
                update = true;
            }
              if(d[e.to]!=INF&&d[e.from]>d[e.to]+e.ed){
                d[e.from]=d[e.to]+e.ed;
                update = true;
            }
        }
        if(!update) break;
    }
}
int main()
{
    //freopen("input.txt","r",stdin);
    cin>>E>>V;
    for(int i=0;i<E;i++)
        cin>>no[i].from>>no[i].to>>no[i].ed;

    bellman_ford(1);
    cout<<d[V]<<endl;
    return 0;
}

poj 2253 Frogger 

题意:起点到终点所有路径中最大跳跃距离的最小值
核心在floyd 的变换方程:mp[i][j]=min(mp[i][j],max(mp[i][k],mp[k][j]));

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

double mp[210][210];
double cal(int x1,int y1,int x2,int y2)
{
  return sqrt(double(x1-x2)*(x1-x2)+double(y1-y2)*(y1-y2));
}
void floyd(int n)
{
    for(int k=0;k<n;k++)
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                //起点到终点所有路径中最大跳跃距离的最小值
                mp[i][j]=min(mp[i][j],max(mp[i][k],mp[k][j]));

}
int main()
{
    int n,x[210],y[210];
    int Case=1;
   // freopen("input.txt","r",stdin);
    while(cin>>n&&n)
    {
        memset(mp,0,sizeof(mp));
        for(int i=0;i<n;i++)
            cin>>x[i]>>y[i];
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                mp[i][j]=mp[j][i]=cal(x[i],y[i],x[j],y[j]);
            }
        }
        floyd(n);
        printf("Scenario #%d\nFrog Distance = %.3lf\n",Case++,mp[0][1]);
        printf("\n");
    }
    return 0;
}
void Dijkstra(int n)
{
    memset(vis,0,sizeof(vis));
   for(int i=1;i<=n;i++)   dis[i]=INF;
    dis[1]=0;
    for(int i=1;i<=n;i++)
    {
        int minn = INF ,k;
        for(int j=1;j<=n;j++)
        {
            if(vis[j]==0 && dis[j]<minn){
                k = j;
                minn = dis[j];
            }
        }
        vis[k]=1;
        for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],max(dis[k],mp[k][j]));
    }
}

poj 1797 Heavy Transportation

题意:n条公路,要把物品从公路1运送到公路n 每条路都有其最大承载量 要求将物品从1运到N的过程,一次所能运的最多的货物

即:求从顶点1到顶点n的  所有可行路径中  各边权值的最小值的最大值

poj 2253类似,但这道题用floyd会T

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int Max = 1e3+10;

int mp[Max][Max];
int vis[Max],dis[Max];

void Dijkstra(int n,int m)
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=mp[1][i];
        vis[i]=0;
    }
    int Max,v;
    for(int i=1;i<=n;i++)
    {
        Max = -1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j] && dis[j]>Max)
            {
                Max=dis[j];
                v = j;
            }
        }
        vis[v] = 1;

        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<min(dis[v],mp[v][j] ) )
                dis[j] = min(dis[v],mp[v][j]);
        }
    }
}
int main()
{
    int T;
    int n,m,x,y,z;
    int Case = 1;
  //  freopen("input.txt","r",stdin);
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        memset(mp,0,sizeof(mp));
        for(int i=0;i<m;i++)
         {
            cin>>x>>y>>z;
            mp[x][y]=mp[y][x]=z;
         }
         Dijkstra(n,m);
         printf("Scenario #%d:\n",Case++);
         cout<<dis[n]<<endl;
         cout<<endl;
    }
    return 0;
}

注意:求最大当中的最小(poj2253),和求最小当中的最大(poj1797),dis的初始值不一样。

poj 3268 Silver Cow Party

题意:有编号为1-N的牛,它们之间存在一些单向的路径。给定一头牛的编号,其他牛要去拜访它并且拜访完之后要返回自己原来的位置,求这些牛中所花的最长的来回时间是多少。

每头牛返回的最短时间很简单就可以算出来,这相当于从目标牛为起点求单源最短路径。但每头牛出发到目标牛的最短时间无法直接算出来,稍微转换一下,发现这个最短时间其实可以通过把所有的边取反向,然后再从目标牛求一次单源最短路径得到。得到这两个最短路径之后,取它们的和的最大者即可。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int Max = 1e3+10;
int N,M,X;
int cost[Max][Max];
int vis[Max],dis[Max],bdis[Max];
void Dijkstra()
{
    for(int i=1;i<=N;i++)
    {
        vis[i]=0;
        dis[i]=cost[X][i];
        bdis[i]=cost[i][X];
    }
    for(int i=1;i<=N;i++)
    {
        int mi=INF,v;
        for(int j=1;j<=N;j++)
        {
            if(!vis[j]&&dis[j]<mi){
                mi=dis[j];
                v = j;
            }
        }
        vis[v]=1;
        for(int j=1;j<=N;j++)
        {
            if(vis[j]==0)
                dis[j]=min(dis[j],dis[v]+cost[v][j]);
        }
    }
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=N;i++)
    {
        int mi=INF,v;
        for(int j=1;j<=N;j++)
        {
            if(!vis[j]&&bdis[j]<mi){
                mi=bdis[j];
                v = j;
            }
        }
        vis[v]=1;
        for(int j=1;j<=N;j++)
        {
            if(vis[j]==0)
                bdis[j]=min(bdis[j],bdis[v]+cost[j][v]);
           //注意这个地方,现在是反着走,cost不要写成cost[v][j]了,这里WA了一下午
        }
    }
    int ma = -1;
    for(int i=1;i<=N;i++)
    {
        if(dis[i]+bdis[i]>ma)
            ma = dis[i]+bdis[i];
    }
    cout<<ma<<endl;
}
int main()
{
    int from,to;
    cin>>N>>M>>X;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            if(i==j) cost[i][j]=0;
            else
                cost[i][j]=INF;
    for(int i=1;i<=M;i++)
    {
        cin>>from>>to;
        cin>>cost[from][to];
    }
    Dijkstra();

    return 0;
}

poj 1860 Currency Exchange

题意:输入n,m,s,v分别代表:有n种货币,有m个地方可以进行货币交换,      你起始的货币种类,你起始货币种类的数目      循环m次接下来输入a,b,rab,cab,rba,cba分别代表a——>b交换      rab:把a换成b的汇率,cab:a换成b的手续费;      问经过重复的交换是否可以使初始货币的总量增加

分析:一种货币就是一个点一个“兑换点”就是图上两种货币之间的一个      兑换方式,是双边,但A到B的汇率和手续费可能与B到A的汇率和手续费不同。      唯一值得注意的是权值,当拥有货币A的数量为V时,A到A的权值为K,即没有兑换      而A到B的权值为(V-Cab)*Rab本题是“求最大路径”,之所以被归类为“求最小路径”      是因为本题题恰恰与bellman-Ford算法的松弛条件相反,求的是能无限松弛的最大正      权路径,但是依然能够利用bellman-Ford的思想去解题。      因此初始化dis(S)=V   而源点到其他点的距离(权值)初始化为无穷小(0),      当s到其他某点的距离能不断变大时,说明存在最大路径;如果可以一直变大,      说明存在正环。判断是否存在环路

注意:要两次使用floyd。第一次用,是本金经过其他总换点的兑换,其他币的价值,并且存下来;

第二次用floyd算法,是为了,和第一个用,作比较,看看其他币种的价值是否增加,如果是,那么判断是正权回路;这么做的目的,判断正权回路,不单单是经过源点的正权回路,还有其他币种的正权回路;

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int Max = 105;

int n,m,s;
double mp[Max],C[Max][Max],R[Max][Max],v;
//mp[]存的是权值 (mp[]-Cab)*Rab
int floyd()
{
    double dis[Max];
    for(int i=1;i<=n;i++) dis[i] = mp[i];
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if((mp[i]-C[i][j])*R[i][j] > mp[j])
                    mp[j]=(mp[i]-C[i][j])*R[i][j];

    for(int i=1;i<=n;i++)//判断有无正环
        if(dis[i]<mp[i])
            return 1;
        return 0;
}
int main()
{
    int a,b;
    double c,d,e,f;
    cin>>n>>m>>s>>v;
    memset(mp,0,sizeof(mp));
    memset(R,0,sizeof(R));
    memset(C,0,sizeof(C));
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c>>d>>e>>f;
        R[a][b]=c;C[a][b]=d;
        R[b][a]= e;C[b][a]=f;
    }
    mp[s]=v;
    floyd();
    if(floyd()) printf("YES\n");
    else printf("NO\n");
    return 0;
}

hdu 3790 最短路径问题

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int Max = 1e3+10;
const int INF = 0x3f3f3f3f;
int V,E;
struct node{
    int from,to,ed,cost;
};
node no[100010];
int dis[Max],spent[Max];
void bellman(int s)
{
    memset(dis,0x3f,sizeof(dis));
    memset(spent,0,sizeof(spent));
    dis[s]=0;
    while(true)
    {
        bool update = false;
        for(int i=0;i<E;i++)
        {
            node e = no[i];
            if(dis[e.to]>dis[e.from]+e.ed){
                dis[e.to]=dis[e.from]+e.ed;
                spent[e.to]=spent[e.from]+e.cost;
                update = true;

            } else if((dis[e.to]==dis[e.from]+e.ed)&&(spent[e.to]>spent[e.from]+e.cost)){
                spent[e.to]=spent[e.from]+e.cost;
                update = true;
            }

            if(dis[e.from]>dis[e.to]+e.ed){
                dis[e.from]=dis[e.to]+e.ed;
                spent[e.from]=spent[e.to]+e.cost;
                update = true;
            }else if((dis[e.from]==dis[e.to]+e.ed)&&(spent[e.from]>spent[e.to]+e.cost)){
                spent[e.from]=spent[e.to]+e.cost;
                update = true;
            }
        }
        if(!update) break;
    }
}

int main()
{
    while(~scanf("%d%d",&V,&E)){
            if(V==0&&E==0) break;
        for(int i=0;i<E;i++)
           scanf("%d%d%d%d",&no[i].from,&no[i].to,&no[i].ed,&no[i].cost);
            int s,en;
            scanf("%d%d",&s,&en);
        bellman(s);
           printf("%d %d\n",dis[en],spent[en]);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值