习题日常第二十六练

1全源最短路(题目链接

开学,考试耽误了一段时间,最近在做简单的题找手感。今天这个题也是最近比较正式的做的一个题,而且这个题是一个johnson的模板题,通过这个题我学会了如何用迪杰斯特拉算法求边权为负的最短路。不得不说johnson算法确实十分的巧妙,今天初学理解不深。它在原有点的基础上加一个虚点,初始化它到每个点的距离都是0,然后用spfa算法(可以优化,并同时判断是否有负环)求一下虚点到每个点的距离。到i点的距离记为h[i]。若每条边的起始点记为s,指向点记为t。这条边的距离加上h[s]-h[t],因为加这个数字是常数,所以并不影响他们之间最短路,这个距离必定为正(具体原因自己想)。然后再跑一边迪杰斯特拉即可。具体代码如下。

#include<cmath>

#include <iostream>

#include<stdio.h>

#include<string>

#include<string.h>

#include<vector>

#include<set>

#include<map>

#include<cstring>

#include<math.h>

#include<stack>

#include<algorithm>

#include<queue>

#include<bitset>

#include<sstream>

#define  ll long long int

const ll mod=20123;

using namespace std;

ll a[5010],b[5020],c[5020],n,m,tot=0,dist[5010],dis[5010],zh[5010];

priority_queue<pair<ll,ll> >p;

deque<ll> q;

struct node

{

    ll next,y,w,x;

}t[20010];

void add(ll x,ll y,ll z)

{

    t[++tot].next=a[x];

    t[tot].x=x;

    t[tot].w=z;

    t[tot].y=y;

    a[x]=tot;

}

int main()

{

    ios::sync_with_stdio(false);

    ll i=0,j,flat=0,x,k,y,z;

    cin>>n>>m;

     for(i=0;i<=n;i++)

        a[i]=-1;

    for(i=0;i<m;i++)

    {

        cin>>x>>y>>z;

        add(x,y,z);

    }

    memset(zh,0,sizeof(zh));

    memset(c,0,sizeof(c));

    q.push_back(0);

    b[0]=1;

    c[0]++;

    for(i=0;i<=n;i++)

        dis[i]=1;

    for(i=1;i<=n;i++)

        add(0,i,0);

        dis[0]=0;

    while(!q.empty())

    {

      ll xp=q.front();

      q.pop_front();

      b[xp]=0;

      if(c[xp]>n)

      {

        flat=1;

      }

      if(flat)

        break;

      for(j=a[xp];j!=-1;j=t[j].next)

      {

          if(dis[t[j].y]>dis[xp]+t[j].w)

          {

              dis[t[j].y]=dis[xp]+t[j].w;

              if(b[t[j].y]==0)

              {

                 q.push_back(t[j].y);

                 b[t[j].y]=1;

                 c[t[j].y]++;

              }

          }

      }

    }

      if(flat)

      {

        cout<<-1;

      }

    else

    {

    for(i=1;i<=n;i++)

        zh[i]=dis[i];

    for(i=0;i<=tot;i++)

    {

        t[i].w+=zh[t[i].x]-zh[t[i].y];

    }

    for(i=1;i<=n;i++)

    {

        for(j=1;j<=n;j++)

        {

            dis[j]=1000000000;

        }

        memset(b,0,sizeof(b));

        dis[i]=0;

        p.push(make_pair(0,i));

        b[i]=0;

        while(!p.empty())

        {

            ll xp=p.top().second;

            p.pop();

            if(b[xp])

                continue;

                b[xp]=1;

            for(j=a[xp];j!=-1;j=t[j].next)

            {

                if(dis[t[j].y]>dis[xp]+t[j].w)

                {

                    dis[t[j].y]=dis[xp]+t[j].w;

                    p.push(make_pair(-dis[t[j].y],t[j].y));



                }

            }

        }

        for(j=1;j<=n;j++)

        {

            if(dis[j]<1e9)

            {

                dis[j]+=zh[j]-zh[i];

            }

            dist[i]+=dis[j]*j;

        }

    }





        for(i=1;i<=n;i++)

            cout<<dist[i]<<endl;

    }

  return 0;

}

2跑路(题目链接

这个题一看是个最短路,但是这个路的长短和平常的长短不一样。若俩点距离为2的幂次,则需要一步可到,所以我们只需要计算哪些点可以一步到,用动态规划的思想可以解决。最后再跑最短路即可。数据规模不大,所以用佛洛依德算法即可。代码如下。

#include<cmath>

#include <iostream>

#include<stdio.h>

#include<string>

#include<string.h>

#include<vector>

#include<set>

#include<map>

#include<cstring>

#include<math.h>

#include<stack>

#include<algorithm>

#include<queue>

#include<bitset>

#include<sstream>

#define  ll long long int

const ll mod=20123;

using namespace std;

ll a[5010],n,m,tot=0,dis[60][60],b[60][60][100];

priority_queue<pair<ll,ll> >p;

struct node

{

    ll next,y,w,x;

}t[20010];

void add(ll x,ll y,ll z)

{

    t[++tot].next=a[x];

    t[tot].x=x;

    t[tot].w=z;

    t[tot].y=y;

    a[x]=tot;

}

int main()

{

    ios::sync_with_stdio(false);

    ll i=0,j,flat=0,x,k,y,z;

    cin>>n>>m;

    memset(dis,0x3f,sizeof(dis));

    for(i=1;i<=m;i++)

    {

        cin>>x>>y;

        dis[x][y]=1;

        b[x][y][0]=1;

    }

    for(x=1;x<=64;x++)

    {

        for(i=1;i<=n;i++)

        {

            for(j=1;j<=n;j++)

            {

                for(k=1;k<=n;k++)

                {

                  if(b[j][i][x-1]&&b[i][k][x-1])

                  {

                      dis[j][k]=1;

                      b[j][k][x]=1;

                  }

                }

            }

        }

    }

     for(int i=1;i<=n;i++)

        for(int j=1;j<=n;j++)

            for(int k=1;k<=n;k++)

                dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);

                cout<<dis[1][n];

  return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值