[图论]Dijkstra 算法小结

Dijkstra 算法小结 

 

By Wine93 2013.11

1. Dijkstra 算法相关介绍

算法阐述Dijkstra是解决单源最短路径的算法,它可以在O(n^2)内计算出源点(s)到图中任何顶点的最短路,但是该算法不能处理存在负权边的图(证明中会给出)

Dijkstra一般有2种实现,一种采用邻接矩阵,复杂度为O(n^2),这种实现适用于稠密图 (边多点少),还有一种是采用临接表+heap(可用优先队列代替)实现,实现的复杂度为( m*log(n) )   (m为边数,n为顶点数),该实现适用于稀疏图(边少点多),各有各的优缺,视实际情况选择.

 算法简单证明:Dijkstra2张表(OPEN,CLOSE),我们可以认为一个表存储已经已经计算出最短路径的顶点(假设U),而另一个则存储没有计算出最短路径的顶点(假设V)Dijkstra每次都取出具有最短路径的顶点(假设NOW),视其就是该顶点的最短路.因为在当前U表中全部扩展的顶点中,NOW顶点是路径最短的顶点,也就是说,其他当前全部可以到达的顶点中,只有大于或等于NOW顶点,如果 通过这些顶点再到达NOW顶点,势必会比现在NOW顶点的路径长,因为原来就大于等于NOW顶点了所以NOW顶点路径长度就是源点到该顶点的最短路径.但是如果存在负权边,则会出现通过其他顶点到达NOW顶点的路径长度小于当前NOW顶点的路径长度,这就是为什么Dijkstra不能处理负权边了(请读者看下面例图,模拟下Dijkstra的执行过程)

 [图论]Dijkstra 算法小结

 

 

2.Dijkstra的相关应用举例

.基础题   

POJ 1511 Invitation Cards 

[图论]Dijkstra 算法小结
# include<cstdio>
# include<cstring>
# include<algorithm>
# include<queue>
# include<map>
# include<vector>
# include<utility>
using namespace std;

# define LL long long
const LL inf=1LL<<62;
const int maxn=1000005;
const int maxm=1000005;
typedef pair<int,LL> node;

struct edge
{
    int u,v,next;
    LL w;
} e[maxm];

struct cmp
{
    bool operator()(const node &a,const node &b)const
    {
        return a.second>b.second;
    }
};

int num,head[maxn];
LL dis[maxn];
int n,m;
bool vis[maxn];
priority_queue<node,vector<node>,cmp> q;

inline void addedge(int u,int v,LL w)
{
    e[num].u=u;
    e[num].v=v;
    e[num].w=w;
    e[num].next=head[u];
    head[u]=num++;
}
    

void dijkstra(int s)
{
    int i,u,v;
    for(int i=0;i<=n;i++)
    {
        dis[i]=inf;
        vis[i]=0;
    }
    dis[s]=0;
    q.push(make_pair(s,dis[s]));
    while(!q.empty())
    {
        u=q.top().first;
        q.pop();
        if(vis[u])
            continue;
        vis[u]=1;
        for(i=head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            if(!vis[v]&&dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                q.push(make_pair(v,dis[v]));
            }
        }    
    }
}

void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}

int main()
{
   //freopen("in.txt","r",stdin);
    vector<edge> vec;
    int T,u,v,i;
    LL w;
    LL ans;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        vec.clear();
        init();
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%I64d",&u,&v,&w);
            addedge(u,v,w);
        }
        dijkstra(1);
        for(i=1;i<=n;i++)
            ans+=dis[i];
        for(i=0;i<num;i++)
            vec.push_back(e[i]);
        init();
        for(i=0;i<vec.size();i++)
            addedge(vec[i].v,vec[i].u,vec[i].w);
        dijkstra(1);
        for(i=1;i<=n;i++)
            ans+=dis[i];
        printf("%I64d\n",ans);
    }
    return 0;
}
POJ 1511

POJ 3013 Big Christmas Tre(重点在于思维的转换)

[图论]Dijkstra 算法小结
# include<cstdio>
# include<cstring>
# include<map>
# include<vector>
# include<queue>
# include<algorithm>
using namespace std;

# define LL long long
typedef pair<LL,int> PII;
# define INF 1LL<<62
# define N 50005
# define M 100005

int num,head[N];
int vis[N];
LL dis[N];
priority_queue< PII,vector<PII>,greater<PII> >q;

struct edge
{
    int u,v,next;
    LL w;
}e[M];

void addedge(int u,int v,LL w)
{
    e[num].u=u;
    e[num].v=v;
    e[num].w=w;
    e[num].next=head[u];
    head[u]=num++;
}

void dijkstra(int n,int s)
{
    int i,u,v;
    for(i=0;i<=n;i++)
    {
        dis[i]=INF;
        vis[i]=0;
    }
    while(!q.empty()) q.pop();
    dis[s]=0;
    q.push(PII(dis[s],s));
    while(!q.empty())
    {
        u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(i=head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            if(!vis[v]&&dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                q.push(PII(dis[v],v));
            }
        }
    }
}

void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T,i,n,m,u,v,flag;
    LL w,ans,pri[N];
    scanf("%d",&T);
    while(T--)
    {
        flag=1;
        ans=0;
        init();
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%I64d",&pri[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%I64d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        dijkstra(n,1);
        for(i=1;i<=n;i++)
            if(dis[i]==INF)
            {
                flag=0;
                break;
            }
        if(!flag) printf("No Answer\n");
        else
        {
            for(i=1;i<=n;i++)
                ans+=pri[i]*dis[i];
            printf("%I64d\n",ans);
        }
    }
    return 0;
}
POJ 3013

.变形题   

POJ 1797 Heavy Transportation

[图论]Dijkstra 算法小结
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;

# define N 1005
# define M 1000005

int num,head[N]; 
int n,m;
int vis[N];

struct edge
{
    int u,v,w,next;
}e[M];

void addedge(int u,int v,int w)
{
    e[num].u=u;
    e[num].v=v;
    e[num].w=w;
    e[num].next=head[u];
    head[u]=num++;
}

int dfs(int u,int limit)
{
    int i,v;
    vis[u]=1;
    if(u==n) 
        return 1;
    for(i=head[u];i!=-1;i=e[i].next)
    {
        v=e[i].v;
        if(!vis[v]&&e[i].w>=limit)
        {
            if(dfs(v,limit))
                return 1;
        }
    }
    return 0;
}

int pass(int limit)
{
    memset(vis,0,sizeof(vis));
    if(dfs(1,limit))
        return 1;
    return 0;
}

int bin(int l,int r)
{
    int m;
    while(l<=r)
    {
        m=(l+r)>>1;
        if(pass(m))
            l=m+1;
        else
            r=m-1;
    }
    return r;
}

void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}

int main()
{
//    freopen("in.txt","r",stdin);
    int cas,T,i,u,v,w;
    scanf("%d",&T);
    for(cas=1;cas<=T;cas++)
    {
        init();
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        printf("Scenario #%d:\n",cas);
        printf("%d\n\n",bin(0,1000000));
    }
    return 0;
}
POJ 1797 二分
[图论]Dijkstra 算法小结
# include<cstdio>
# include<cstring>
# include<map>
# include<queue>
# include<vector>
# include<algorithm>
using namespace std;

typedef pair<int,int> PII;
# define N 1005
# define M 1000005
priority_queue< PII,vector<PII>,less<PII> > q;  //pair<dis,u> dis从大到小

int num,head[N];
int dis[N];

struct edge
{
    int u,v,w,next;
}e[M];

void addedge(int u,int v,int w)
{
    e[num].u=u;
    e[num].v=v;
    e[num].w=w;
    e[num].next=head[u];
    head[u]=num++;
}

void dijkstra(int n,int s)
{
    int i,u,v;
    for(i=1;i<=n;i++)
        dis[i]=0;
    dis[s]=1<<30;
    q.push(PII(dis[s],s));
    while(!q.empty())
    {
        PII now=q.top();
        u=now.second;
        q.pop();
        if(now.first<dis[u]) continue;
        for(i=head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            if(dis[v]<min(dis[u],e[i].w))
            {
                dis[v]=min(dis[u],e[i].w);
                q.push(PII(dis[v],v));
            }
        }
    }
}

void init()
{
    num=0;
    memset(head,-1,sizeof(head));
}

int main()
{
    //freopen("in.txt","r",stdin);
    int cas,T,i,n,m,u,v,w;
    scanf("%d",&T);
    for(cas=1;cas<=T;cas++)
    {
        init();
        scanf("%d%d",&n,&m);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        dijkstra(n,1);
        printf("Scenario #%d:\n",cas);
        printf("%d\n\n",dis[n]);
    }
    return 0;
}
POJ 1797 Dijkstra

POJ 2253 Frogger

[图论]Dijkstra 算法小结
# include<cstdio>
# include<cstring>
# include<cmath>
# include<map>
# include<queue>
# include<vector>
# include<algorithm>
using namespace std;

# define INF 1<<30
# define N 205

int vis[N];
double dis[N];
double mat[N][N];

struct point
{
    double x,y;
}p[N];

double calc(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

void dijkstra(int n,int s)
{
    double mind;
    int i,j,u,v;
    dis[s]=0;
    for(i=1;i<=n;i++)
    {
        mind=1<<30;
        for(j=1;j<=n;j++)
            if(!vis[j]&&dis[j]<mind)
            {
                mind=dis[j];
                u=j;
            }
        vis[u]=1;
        for(v=1;v<=n;v++)
            if(!vis[v]&&dis[v]>max(dis[u],mat[u][v]))
                dis[v]=max(dis[u],mat[u][v]);
    }
}

void init(int n)
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        vis[i]=0;
        dis[i]=INF;
        for(j=1;j<=n;j++)
            mat[i][j]=INF;
    }
}

int main()
{
   // freopen("in.txt","r",stdin);
    int i,j,n,cas=1;
    while(scanf("%d",&n)!=EOF&&n)
    {
        init(n);
        for(i=1;i<=n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        for(i=1;i<=n;i++)
            for(j=i+1;j<=n;j++)
                mat[i][j]=mat[j][i]=calc(p[i],p[j]);
        dijkstra(n,1);
        printf("Scenario #%d\n",cas++);
        printf("Frog Distance = %.3lf\n\n",dis[2]);
    }
    return 0;
}
POJ 2253

    将这2题放在一起讲(2题都可用2分解),我们可以利用Dijkstra特殊的结构解决一类问题----单调路径(况且这么叫),所谓单调路径,即为对于任何一条路径我们所求的值只会成单调变化

POJ1797来说,每经过一条路径,weight的值只会减少(甚至不变),不会增加,看下图(边权值表示 weight,红色顶点为起点,绿色为终点,顶点下数字表示从起始点到达该顶点的weight)

[图论]Dijkstra 算法小结

我们可以发现weight呈现非递增,而我们要求的则是weight的最大值,也就是说我们求的和它的趋势相反都可用Dijkstra来题解(请看下图),这跟Dijkstra可用heap优化是异曲同工(可以认真理解证明)

[图论]Dijkstra 算法小结[图论]Dijkstra 算法小结

 

如果所求值和趋势为同一走向则不能用Dijkstra求解(如最长路)

 三.好题

POJ 3463 Sightseeing

 

3.个人心得

Dijkstra的变形和应用非常多,需要一定的时间和题量积累,但是只要能深入理解Dijkstra的贪心 策略以及他在单调路径上(况且这么叫)的作用,很多问题都会迎刃而解

[图论]Dijkstra 算法小结

上一篇:google的多次点击实现


下一篇:【树莓派】获取未知设备的IP地址