最短路总结&&模板

先放dijatra算法吧,算是最常用的
dijstra算法本质上就是bfs+贪心…
注意图中有负权的话,这个算法就会出问题了…那就要用别的算法了…
这个算法的策略是
每次找出当前没有被标记出来的最近的点,然后把这个点标记上
之后用这个点去松弛其他没有被标记过的点,直到所有的点都被标记上,就出来最优结果了
朴素n2算法

int dis[maxn],vis[maxn];
int arr[maxn][maxn];
void init()
{
    ms(dis,inf),ms(vis,0),ms(arr,inf);
}
void dijstra(int st,int len)
{
    vis[st]=1;
    dis[st]=0;
    rep(i,1,n)
    {
    	dis[i]=arr[st][i];
    }
    while(1)
    {
        int pos=-1;
        for(int i=1;i<=len;i++)
        {
            找到没有被标记过的  距离最近的点
            if(!vis[i]&&(pos==-1||dis[pos]>dis[i]))
            {
                pos=i;
            }
        }
        if(pos==-1)
        {
            break;
        }
        vis[pos]=1;
        for(int i=1;i<=len;i++)
        {
            如果没有被标记过  且可以松弛进一步缩短距离
            if(!vis[i]&&dis[i]>dis[pos]+arr[pos][i])
            {
                dis[i]=dis[pos]+arr[pos][i];
            }
        }
    }
}
void solve()
{
    init();
    int n=read(),m=read();
    while(m--)
    {
        int a=read(),b=read(),c=read();
        若图无向
        {
            用min函数防止重边
            arr[a][b]=arr[b][a]=min(arr[a][b],c);
        }
        若图有向
        {
            有向的话就只能更新一条路
            arr[a][b]=min(arr[a][b],c);
        }
    }
    dijstra(1,n);
    cout<<dis[n]<<endl;
}

这种算法的时间复杂度是n2,在数据范围稍微大一点的时候就会挂掉。。
而且用邻接矩阵储存地图,那么在数据大的情况下数组也开不了
一般适用于1e3及以内的情况
优先队列+邻接表优化
朴素算法复杂的原因是他每次寻找最近点需要遍历一次全部数组导致复杂度高
我们可以用一个优先队列来优化寻找最近的点
同时我们可以用邻接表储存地图来适用于大数据
上模板吧 刚学会的优化方式…还要多熟悉
这个算法是可以处理重边的~

struct node
{
	int from;
    int to;
    int cost;
    int nx;
}edge[maxn*2];  有向图不用乘于2  无向图需要乘于2
int vis[maxn],dis[maxn],head[maxn];
int cnt;
void add(int st,int ed,int val)
{
	邻接表储存
    cnt++;
    edge[cnt].from=st;
    edge[cnt].to=ed;
    edge[cnt].cost=val;
    edge[cnt].nx=head[st];
    head[st]=cnt;
}
void init()
{
    ms(vis,0),ms(head,-1),ms(dis,inf);
}
struct cmp
{
    bool operator()(const int a,const int b)
    {
        return dis[a]>dis[b];
    }
};
void dijstra(int st)
{
    dis[st]=0;
    priority_queue<int,vector<int>,cmp>pq;
    pq.push(st);
    while(!pq.empty())
    {
        int now=pq.top();
        pq.pop();
        if(vis[now])
        {
            continue;
        }
        vis[now]=1;
        for(int i=head[now];i!=-1;i=edge[i].nx)
        {
            int to=edge[i].to;
            int cost=edge[i].cost;
            if(!vis[to]&&dis[to]>dis[now]+cost)
            {
                dis[to]=dis[now]+cost;
                pq.push(to);
            }
        }
    }
}
void solve()
{
    init();
    cnt=0;
    int t=read(),n=read();
    while(t--)
    {
        int a=read(),b=read(),c=read();
        add(a,b,c);
        add(b,a,c);
    }
    dijstra(1);
    cout<<dis[n]<<endl;
}

----------------------------------
spfa算法
经过我一天多的刷题,慢慢学会了spfa这个算法
首先我们知道dijstra不可以处理带负权的图,而spfa可以处理带负权的图
但两者都不能处理负环的图,spfa只是可以判断是否有负环,这点我们放在后面说
先上spfa朴素版代码

int arr[maxn][maxn];
int vis[maxn],dis[maxn];
int n,m;
void spfa(int st)
{
    rep(i,1,n)
    {
        vis[i]=0;
        dis[i]=inf;
    }
    vis[st]=1;
    dis[st]=0;
    queue<int>que;
    que.push(st);
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        vis[now]=0;
        rep(i,1,n)
        {
            if(dis[i]>dis[now]+arr[now][i])
            {
                dis[i]=dis[now]+arr[now][i];
                if(!vis[i])
                {
                    vis[i]=1;
                    que.push(i);
                }
            }
        }
    }
}
void solve()
{
    m=read(),n=read();
    rep(i,1,n)
    {
        rep(j,1,n)
        {
            arr[i][j]=inf;
            if(i==j)
            {
                arr[i][j]=0;
            }
        }
    }
    while(m--)
    {
        int a=read(),b=read(),c=read();
        ///注意重边问题!!
        arr[a][b]=arr[b][a]=min(arr[a][b],c);
    }
    spfa(1);
    printf("%d\n",dis[n]);
}

注意初始化数组的位置,以及判断重边问题,邻接矩阵写法需要自己手动判断重边
接下来是spfa算法的邻接表优化方法

struct node
{
    int from;
    int to;
    int cost;
    int nx;
}edge[maxm*2];
int vis[maxn],dis[maxn],head[maxn];
int num=0;
void add(int st,int ed,int val)
{
    num++;
    edge[num].from=st;
    edge[num].to=ed;
    edge[num].cost=val;
    edge[num].nx=head[st];
    head[st]=num;
}
void spfa(int st)
{
    vis[st]=1,dis[st]=0;
    queue<int>que;
    que.push(st);
    while(!que.empty())
    {
        int now=que.front();
        vis[now]=0;//这里要记住取消标记
        que.pop();
        for(int i=head[now];i!=-1;i=edge[i].nx)
        {
            int to=edge[i].to;
            int cost=edge[i].cost;
            if(dis[to]>dis[now]+cost)
            {
                dis[to]=dis[now]+cost;
                if(!vis[to])
                {
                    vis[to]=1;
                    que.push(to);
                }
            }
        }
    }
}
void init()
{
    ms(vis,0);
    ms(head,-1);
    ms(dis,inf);
}
void solve()
{
    int m=read(),n=read();
    init();
    while(m--)
    {
        int a=read(),b=read(),c=read();
        add(a,b,c);
        add(b,a,c);
    }
    spfa(1);
    printf("%d\n",dis[n]);
}

关于spfa判断重边问题,我们只需要单独开一个数组cnt来记录每个点进入队列的次数,当大于等于n时则表明存在负环

if(cnt[now]>=n)
{
     return true;
}

spfa代码是

//判断是否有负环
bool spfa(int st)
{
    vis[st]=1;
    dis[st]=0;
    queue<int>que;
    que.push(st);
    cnt[st]++;
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        vis[now]=0;
        if(cnt[now]>=n)
        {
            return true;
        }
        for(int i=head[now];i!=-1;i=edge[i].nx)
        {
            int to=edge[i].to;
            int cost=edge[i].cost;
            if(dis[to]>dis[now]+cost)
            {
                dis[to]=dis[now]+cost;
                if(!vis[to])
                {
                    vis[to]=1;
                    que.push(to);
                    cnt[to]++;
                }
            }
        }
    }
    return false;
}

先记录到这吧,之后再添加一个be…啥的专门处理带环的算法,floyd就不写了吧。。那个太容易理解了…之后关于最短路更多的细节技巧,我们再开一个kuangbin的最短路专题来记录
----------------------------------------------
bellman ford算法也学了
贝尔曼福德算法,负权图专用 判负环专用 不会像spfa一样被卡的算法
我看的这个学的
学会后发现还挺简单,就是有点暴力,关于优化…那不就是spfa了???
刷了俩题
求最短路 POJ 2387
呃 可以用结构体优化一下的,,但我没搞
然后就是注意无向图双加边就好了

void bellmanford(int st,int n,int m){
    rep(i,1,n){
        dis[i]=inf;
    }
    dis[st]=0;
    rep(k,1,n-1){
        int over=1;
        rep(i,1,m){
            if(dis[v[i]]>dis[u[i]]+w[i]){
                over=0;
                dis[v[i]]=dis[u[i]]+w[i];
            }
        }
        //if(!over)break;
    }
}
void solve(){
    int m=read(),n=read();
    rep(i,1,m){
        u[i*2]=read(),v[i*2]=read(),w[i*2]=read();
        u[i*2-1]=v[i*2],v[i*2-1]=u[i*2],w[i*2-1]=w[i*2];
    }
    bellmanford(n,n,m*2);/*
    rep(i,1,n){
        de(dis[i]);
    }*/
    printf("%d\n",dis[1]);
}

判断负环 POJ 3259

struct node
{
    int fr,to,cost;
}edge[maxn];
int dis[maxn];
int n,m,w,num;
void init()
{
    rep(i,1,n)
    {
        dis[i]=inf;
    }
    num=0;
}
void add(int st,int ed,int cost)
{
    num++;
    edge[num].fr=st;
    edge[num].to=ed;
    edge[num].cost=cost;
}
bool bellmanford()
{
    rep(i,1,n){
        dis[i]=inf;
    };
    dis[1]=0;
    rep(i,1,n-1){
        int ok=0;
        rep(j,1,num){
            int fr=edge[j].fr,to=edge[j].to,val=edge[j].cost;
            if(dis[to]>dis[fr]+val){
                dis[to]=dis[fr]+val;
                ok=1;
            }
        }
    }
    rep(i,1,num){
        if(dis[edge[i].to]>dis[edge[i].fr]+edge[i].cost){
            return true;
        }
    }
    return false;
}
void solve()
{
    n=read(),m=read(),w=read();
    init();
    while(m--)
    {
        int a=read(),b=read(),c=read();
        add(a,b,c);
        add(b,a,c);
    }
    while(w--)
    {
        int a=read(),b=read(),c=read();
        add(a,b,-c);
    }
    if(bellmanford())
    {
        puts("YES");
    }
    else 
    {
        puts("NO");
    }
}

猜你喜欢

转载自blog.csdn.net/leoxe/article/details/105312906