zoukankan      html  css  js  c++  java
  • 图论8-最短路练习

    第一道题:P1186 玛丽卡

    题意分析:在一个图中随便求任意断了一条边的最短路的最大值。(最大值是因为有多种断边的方案)。

    思路解析:首先是大暴力,根据题目要求模拟就行了。就是依次枚举每条边,再断开这条边并跑一遍 spfa 或 dijkstra

    ,这样的时间复杂度是 O(m2logm) 可以说妥妥的T。

    但是千里之行始于足下,正解还是要建立在暴力的基础上的。所以现在开始想一想能怎么优化,首先可以确定 spfa 和

    dijkstra 是没法再优化了。所以优化的肯定就是断边的方案,什么样的边肯定不会断呢?可以确定,不在 1-n 最短路上

    的边一定不会断,而这个最短路是其中任意一个最短路就好了。因为如果一条边不在 1-n 的所有最短路上的话断了也是

    白断,只要继续走这条最短路就好了。所以只有在最短路上的边才有被断的“资格”。那怎么确定一条边是不是在最短

    路上呢?可以记录每个点的前缀节点,也就是它可以有什么节点过来是最短路。然后从n开始遍历前缀节点,直到 1 号节

    点,这样遍历的这条路径就是一条最短路。这里注意,有可能有很多最短路,但是只用记录其中一条即可,因为断的边必

    须在所有最短路上才行。

    具体步骤:

    1: 建图

    2: 一遍spfa,找到所有点的前缀节点。

    3: 之后从n枚举,找到最短路径上的边,并删除(标记)这条边,再开始跑 spfa 或 dijkstra (但我写的是 spfa )。

    4: 记录并输出答案

    完整代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int NR=1005;
    const int MR=1e6+10;
    int n,m;
    int to[MR],nxt[MR],val[MR];
    int head[NR];
    int tot=1;//邻接表 
    void add(int x,int y,int z)
    {
        to[tot]=y;
        val[tot]=z;
        nxt[tot]=head[x];
        head[x]=tot++;
    }
    bool flag[NR][NR];//标记数组,表示i—>j这条边被删除了 
    bool vis[NR];
    int la[NR];//前缀节点 
    int dis[NR];
    int ans;
    void spfa(bool f)//正常spfa,但是bool f的作用是是否计算la,也就是前缀节点 
    {
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        queue<int> q;
        q.push(1);
        dis[1]=0;
        vis[1]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(flag[x][y]) continue;
                if(dis[y]>dis[x]+val[i])
                {
                    if(f) la[y]=x;
                    dis[y]=dis[x]+val[i];
                    if(!vis[y])
                    {
                        vis[y]=1;
                        q.push(y);
                    }
                }
            }
        }
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
    //  freopen("1.in","r",stdin);
    //  freopen("1.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read(),z=read();
            add(x,y,z);add(y,x,z);
        }
        spfa(1);
        for(int i=n;i;i=la[i])
        {
            flag[la[i]][i]=1;
            flag[i][la[i]]=1;//标记 
            spfa(0);
            flag[la[i]][i]=0;
            flag[i][la[i]]=0;//解除标记 
            ans=max(ans,dis[n]);//更新答案 
        }
        printf("%d",ans);
        return 0;
    }
    

     

    第二题:P1462 通往奥格瑞玛的道路

    题意简述:有一个图,其中每个点和边都有一个权值,求在边权和不大于 b 时经过的最大点权的最小值是多少。

    思路解析:首先可以确定这个题一定是个与最短路有关的题(非常明显),但这道题和正常的最短路模板比起来就多了

    一个点权限制,否则就是一个模板题。所以我们要将不会的转化为会的,所以我们先忽略点权的事情,相信大家都会做。

    就只要跑一遍最短路就行了。

    但如果已经规定了最大点权那我们会不会呢?当然是会的,只要有一些点不能走不就行了,那么就是遍历到一个节点后

    如果发现这个点的权值大于最大的权值,那么就直接跳过这个点。

    讲到这里,相信大家都会做这道题了,怎么将原题规定最大点权呢?当然是二分答案啦!二分最大点权然后规定点权后跑

    spfa 然后看到 dis[n] 的值是否大于b,来判断这个最大权值是否合法。

    这个算法的时间复杂度是 O(km×log(max{c_i}-min{c_i}) 是能过的。

    再来理一下思路

    step 1: 读入建图

    step 2: 二分最大点权值

    step 3: 在 step2 的点权限制下跑 spfa ,最后再检查 dis[n] 是否大于 b。

    step 4: 输出最小二分值或 AFK。

    完整代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int NR=1e4+10;
    const int MR=1e5+10;
    int n,m,b;
    int a[NR];
    int to[MR],nxt[MR],val[MR];
    int head[NR];
    int tot=1;
    void add(int x,int y,int z)
    {
        to[tot]=y;
        val[tot]=z;
        nxt[tot]=head[x];
        head[x]=tot++;
    }
    bool vis[NR];
    int dis[NR];
    bool spfa(int num)
    {
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        queue<int> q;q.push(1);
        dis[1]=0;vis[1]=1;
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            if(a[x]>num) continue;
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(a[y]>num) continue;
                if(dis[y]>dis[x]+val[i])
                {
                    dis[y]=dis[x]+val[i];
                    if(!vis[y])
                    {
                        vis[y]=1;
                        q.push(y);
                    }
                }
            }
        }
        return dis[n]<b;
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
    //  freopen("1.in","r",stdin);
    //  freopen("1.out","w",stdout);
        n=read(),m=read(),b=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read(),z=read();
            add(x,y,z);add(y,x,z);
        }
        int l=0,r=1000000000,ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(spfa(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        if(ans>0) printf("%d
    ",ans);
        else puts("AFK");
        return 0;
    }
    

      

  • 相关阅读:
    退役了
    Luogu1835 素数密度_NOI导刊2011提高(04
    Luogu1941 飞扬的小鸟
    Luogu4711 「化学」相对分子质量
    CF285E Positions in Permutations
    JZOJ 5944
    BZOJ3827: [Poi2014]Around the world && CF526E Transmitting Levels
    BZOJ3831: [Poi2014]Little Bird
    CF526F Pudding Monsters
    运算符重载
  • 原文地址:https://www.cnblogs.com/chen-1/p/12629286.html
Copyright © 2011-2022 走看看