最短路题记


POJ2387 Til the Cows Come Home(Dijktra)

题目地址:http://poj.org/problem?id=2387
大致题意就是给你一些边让你找到从1到n的最短路径

  • 邻接表
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

typedef pair<int,int>P;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=(int)2e3+10;
int dis[Max_n];

struct Edge{
    int to,cost;
    Edge(int xx,int s):to(xx),cost(s){};
};

vector<Edge>G[Max_n];
priority_queue<P,vector<P>,greater<P> >q;

int main(){
    int t,n;
    scanf("%d%d",&t,&n);
    for(int i=0;i<t;i++){///有重边,但是都会被加进去
        int from,to,cost;
        scanf("%d%d%d",&from,&to,&cost);
        G[from].push_back(Edge(to,cost));
        G[to].push_back(Edge(from,cost));
    }
    memset(dis,inf,sizeof(dis));
    q.push(P(0,1));
    dis[1]=0;
    while(q.size()){
        P p=q.top();q.pop();
        int v=p.second;///当前顶点
        if(dis[v]<p.first) continue;///之前计算的距离<当前的距离
        for(unsigned int i=0;i<G[v].size();i++){
            Edge e=G[v][i];
            if(dis[e.to]>dis[v]+e.cost){///即将到达的点的距离可以更新
                dis[e.to]=dis[v]+e.cost;
                q.push(P(dis[e.to],e.to));
            }
        }
    }
    printf("%d\n",dis[n]);
    return 0;
}
  • 邻接矩阵
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=(int)1e3+10;
int a[Max_n][Max_n],vis[Max_n],dis[Max_n];

int main(){//矩阵实现
    int t,n;
    scanf("%d%d",&t,&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) a[i][j]=0;
            else a[i][j]=inf;
        }
    }
    for(int i=1;i<=t;i++){
        int from,to,cost;
        scanf("%d%d%d",&from,&to,&cost);
        if(cost<a[from][to]){//去重
            a[from][to]=cost;
            a[to][from]=cost;
        }
    }
    for(int i=1;i<=n;i++)
        dis[i]=a[1][i];
    memset(vis,0,sizeof(vis));
    vis[1]=0;
    int u=0;
    for(int i=1;i<=n-1;i++){//再更新n-1个点
        int mmin=inf;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&dis[j]<mmin){//当前最短的距离
                mmin=dis[j];
                u=j;
            }
        }
        vis[u]=1;//根据此点去更新
        for(int v=1;v<=n;v++){
            if(a[u][v]<inf&&dis[v]>dis[u]+a[u][v]){
                dis[v]=dis[u]+a[u][v];
            }
        }
    }
    printf("%d\n",dis[n]);
    return	0;
}

POJ3259 Wormholes(Bellman-Ford&SPFA)

题目地址:http://poj.org/problem?id=3259
题意:题目给你m条正权边,w条负权边.问这个图中是否存在负权圈.正权边是双向的,负权边是单向的.

  • Bellman-Ford
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=(int)1e4+10;
int dis[Max_n];

struct Edge{
    int u,v,w;
}edge[Max_n];

int main(){//Bellman-ford
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,w;
        scanf("%d%d%d",&n,&m,&w);
        int cnt=0;
        int from,to,cost;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&from,&to,&cost);
            edge[++cnt].u=from;edge[cnt].v=to;edge[cnt].w=cost;
            edge[++cnt].u=to;edge[cnt].v=from;edge[cnt].w=cost;
        }
        for(int i=1;i<=w;i++){
            scanf("%d%d%d",&from,&to,&cost);
            edge[++cnt].u=from;edge[cnt].v=to;edge[cnt].w=-cost;
        }
        for(int i=1;i<=n;i++) dis[i]=inf;
        dis[1]=0;
        for(int i=1;i<=n-1;i++){//进行n-1轮松弛
            bool check=false;//检查此轮有没有更新
            for(int j=1;j<=cnt;j++){
                if(dis[edge[j].v]>dis[edge[j].u]+edge[j].w){
                    dis[edge[j].v]=dis[edge[j].u]+edge[j].w;
                    check=true;
                }
            }
            if(!check) break;
        }
        bool flag=false;
        for(int i=1;i<=cnt;i++){
            if(dis[edge[i].v]>dis[edge[i].u]+edge[i].w){
                dis[edge[i].v]=dis[edge[i].u]+edge[i].w;
                flag=true;
            }
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return	0;
}
  • SPFA
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;

typedef long long LL;
typedef pair<int,int>P;
const int inf=0x3f3f3f3f;
const int Max_n=(int)1e4+10;
int dis[Max_n],in[Max_n];
bool vis[Max_n];

vector<P>G[Max_n];
queue<int>q;

bool spfa(int n){
    q.push(1);
    while(!q.empty()){
        int now=q.front();q.pop();vis[now]=false;
        in[now]++;
        if(in[now]>n) return false;
        for(int i=0;i<G[now].size();i++){
            P p=G[now][i];
            if(dis[p.first]>dis[now]+p.second){
                dis[p.first]=dis[now]+p.second;
                if(!vis[p.first]){
                    q.push(p.first);
                }
            }
        }
    }
    return true;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        while(!q.empty()) q.pop();
        int n,m,w;
        scanf("%d%d%d",&n,&m,&w);
        for(int i=0;i<=n;i++) G[i].clear();
        int from,to,cost;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&from,&to,&cost);
            G[from].push_back(P(to,cost));
            G[to].push_back(P(from,cost));
        }
        for(int i=1;i<=w;i++){
            scanf("%d%d%d",&from,&to,&cost);
            G[from].push_back(P(to,-cost));
        }
        for(int i=1;i<=n;i++) dis[i]=inf;
        dis[1]=0;
        memset(vis,0,sizeof(vis));
        memset(in,0,sizeof(in));
        vis[1]=true;
        if(spfa(n)) printf("NO\n");//true没有负权圈
        else printf("YES\n");
    }
    return	0;
}

POJ1860 Currency Exchange(spfa判正环)

题目地址:http://poj.org/problem?id=1860
题意:个人觉得题目描述的意思有点难懂,此题是说,我们有n个兑换点,然后输入m个边(双向的),刚开始我们在兑换点s,有本钱v.我们输入每一个兑换点之间的路,从from到to我们的汇率为a1,佣金为a2,从to到from我们的汇率为b1,佣金为b2.此题最后问我们走过一些兑换点后最后在经过刚开始的兑换点的时候手中的钱会不会变多.我们只要判断这个图中是不是有正环即可.

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

typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=1e3+10;
int in[Max_n];
double dis[Max_n];
bool vis[Max_n];

struct Edge{
    int to;
    double rate,commission;
    Edge(){}//¹¹Ô캯ÊýµÄ¹ø......
    Edge(int to,double rate,double commission):to(to),rate(rate),commission(commission){}
}edge[Max_n];

vector<Edge>G[Max_n];
queue<int>q;

bool spfa(int s,int n,double v){
    for(int i=1;i<=n;i++) dis[i]=0;//注意初始化成零
    memset(in,0,sizeof(in));
    memset(vis,false,sizeof(vis));
    vis[s]=true;
    q.push(s);dis[s]=v;
    while(!q.empty()){
        int p=q.front();q.pop();vis[p]=false;
        in[p]++;
        if(in[p]>n) return true;
        for(unsigned int i=0;i<G[p].size();i++){
            Edge edge=G[p][i];
            if(dis[edge.to]<(dis[p]-edge.commission)*edge.rate){
                dis[edge.to]=(dis[p]-edge.commission)*edge.rate;
                if(!vis[edge.to]){
                    vis[edge.to]=true;
                    q.push(edge.to);
                }
            }
            if(dis[s]>v) return true;
        }
    }
    return false;
}

int main(){
    int n,m,s;
    double v;
    scanf("%d%d%d%lf",&n,&m,&s,&v);
    int from,to;
    double a1,a2,b1,b2;
    for(int i=1;i<=m;i++){
        scanf("%d%d%lf%lf%lf%lf",&from,&to,&a1,&a2,&b1,&b2);
        G[from].push_back(Edge(to,a1,a2));
        G[to].push_back(Edge(from,b1,b2));
    }
    if(spfa(s,n,v)) printf("YES\n");
    else printf("NO\n");
    return 0;
}


POJ2240 Arbitrage(spfa判正环)

题目地址:http://poj.org/problem?id=2240
题意:给你n种货币,然后给你两种货币之间的汇率,最后问你最后是否能够获利.哭辽,WA了一晚上,刚开始忘记加头文件sting,CE一会,后来没有想到题目可以每一种货币都可以判断一边,从那种货币开始判断最后是否能够获利,就在我觉得我的代码毫无破绽的时候,又僵持了几个小时,WA哭了,是在找不到错误在哪里,不知道为什么就点开了discuss,发现输出应该是Yes和No,而我的输出是YES,NO.以后做题需要认真一点!!!(注:有了spfa我觉得很快我就把dijkstra遗忘啦.?尽管二者很像…)

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
using namespace std;

typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=40;
const int Max_m=1e6+10;
int in[Max_n];
double dis[Max_n];
bool vis[Max_n];

struct Edge{
    int to;
    double cost;
    Edge(){}
    Edge(int to,double cost):to(to),cost(cost){}
};

map<string,int>ma;
vector<Edge>G[Max_m];
queue<int>q;

bool spfa(int n,int pos){
    memset(in,0,sizeof(in));
    for(int i=1;i<=n;i++) dis[i]=0.0;
    memset(vis,false,sizeof(vis));
    vis[pos]=true;dis[pos]=1.0;
    q.push(pos);
    while(!q.empty()){
        int p=q.front();q.pop();vis[p]=false;
        in[p]++;
        if(in[p]>n) return true;
        for(unsigned int i=0;i<G[p].size();i++){
            Edge edge=G[p][i];
            if(dis[edge.to]<dis[p]*edge.cost){
                dis[edge.to]=dis[p]*edge.cost;
                if(!vis[edge.to]){
                    q.push(edge.to);
                    vis[edge.to]=true;
                }
            }
            if(dis[pos]>1.0) return true;
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false);
    int n;
    int cnt=0;
    while(cin>>n&&n){
        cnt++;
        for(int i=1;i<=n;i++)
            G[i].clear();
        for(int i=1;i<=n;i++){
            string str;
            cin>>str;
            ma[str]=i;
        }

        int m;
        cin>>m;
        map<string,int>::iterator it1,it2;
        for(int i=1;i<=m;i++){
            string from,to;
            double cost;
            cin>>from>>cost>>to;
            G[ma[from]].push_back(Edge(ma[to],cost));
        }
        bool flag=false;
        for(int i=1;i<=n;i++){
            if(spfa(n,i)){
               flag=true;
               break;
            }
        }
        cout<<"Case "<<cnt<<": ";
        if(flag) cout<<"Yes"<<endl;//注意输出
        else cout<<"No"<<endl;
    }
    return 0;
}

POJ 1724 ROADS(Dijkstra-带限制)

在这里插入图片描述
题目地址:http://poj.org/problem?id=1724
…还不让写个博客了,POJ不支持万能头文件…
题意:我们手中总钱数有k,现在我们有n个点和m条边,对于m条边我们输入起点、终点、距离和花费.题目要求我们在花费不超过k的情况下找到1-n的最短路.我们可以以花费作为判断条件,用堆来维护,如果在更新路径时的花费少于原来的花费,那么我们就把边入队,这样一来,当我们在队列中遇到第一个n时,他所表示的路径就是在限制条件下的最短路径.

#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
using namespace std;

typedef long long LL;
const int inf=0x3f3f3f3f;
const int Max_n=1e4+10;
int dis[Max_n];

struct Edge{
    int v,w,cost;
    Edge(){};
    Edge(int v,int w,int cost):v(v),w(w),cost(cost){}
    bool operator <(const Edge &a) const{
        if(w==a.w)
            return cost<a.cost;
        return w>a.w;
    }
}edge[Max_n];

int n,m,k;

vector<Edge>G[Max_n];
priority_queue<Edge>q;

int Dijkstra(){
    q.push(Edge(1,0,0));
    while(!q.empty()){
        Edge now=q.top();q.pop();
        if(now.v==n)
            return now.w;
        for(unsigned int i=0;i<G[now.v].size();i++){
            Edge edge=G[now.v][i];
            if(now.cost+edge.cost<=k){//这里now.cost写成now.w
                q.push(Edge(edge.v,now.w+edge.w,now.cost+edge.cost));
                //printf("%d %d %d\n",edge.v,now.w+edge.w,now.cost+edge.cost);
            }
        }
    }
    return inf;
}

int main(){
    scanf("%d",&k);
    scanf("%d",&n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int s,d,l,t;
        scanf("%d%d%d%d",&s,&d,&l,&t);
        G[s].push_back(Edge(d,l,t));
    }
    int ans=Dijkstra();
    if(ans==inf) printf("-1\n");
    else printf("%d\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值