• Luogu P2149 [SDOI2009]Elaxia的路线(最短路+记忆化搜索)


    P2149 [SDOI2009]Elaxia的路线

    题意

    题目描述

    最近,(Elaxia)(w**)的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们必须合理地安排两个人在一起的时间。

    (Elaxia)(w**)每天都要奔波于宿舍和实验室之间,他们希望在节约时间的前提下,一起走的时间尽可能的长。

    现在已知的是(Elaxia)(w**)所在的宿舍和实验室的编号以及学校的地图:地图上有(N)个路口,(M)条路,经过每条路都需要一定的时间。具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

    输入输出格式

    输入格式:

    第一行:两个整数(N)(M)(含义如题目描述)。

    第二行:四个整数(x_1,y_1,x_2,y_2(1leq x_1 leq N,1leq y_1leq N,1leq x_2leq N,1leq y_2leq N)),分别表示(Elaxia)的宿舍和实验室及(w**)的宿舍和实验室的标号(两对点分别(x1,y1)(x2,y2))。

    接下来(M)行:每行三个整数,(u,v,l(1leq uleq N,1leq vleq N,1leq lleq 10000)),表示(u)(v)之间有一条路,经过这条路所需要的时间为(l)

    输出格式:

    一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)

    输入输出样例

    输入样例#1:

    9 10
    1 6 7 8
    1 2 1
    2 5 2
    2 3 3
    3 4 2
    3 9 5
    4 5 3
    4 6 4
    4 7 2
    5 8 1
    7 9 1
    

    输出样例#1:

    3
    

    说明

    对于(30\%)的数据,(Nleq 100)

    对于(60\%)的数据,(Nleq 1000)

    对于(100\%)的数据,(Nleq 1500),输入数据保证没有重边和自环。

    思路

    等我再调一下,我只有一个小错误了... --alecli

    首先想想题目要求我们做什么。我们要求两组点对之间的最短路,每组点对之间的最短路可能有很多条。然后我们在每组点对中各选出一条最短路,使得它们之间的公共边的长度之和最长,并输出这个长度。

    那么我们的算法流程就是:

    1. 找出第一组点对之间的所有最短路径。
    2. 找出第二组点对之间的所有最短路径。
    3. (dfs)选两条路径,计算公共边长度。

    选出路径的复杂度已经很高了,再去计算公共边长度,时间就爆炸了。能不能优化一下呢?我们不妨这样写:

    1. 找出第一组点对之间的所有最短路径。
    2. 把这些最短路径经过的边都打上标记。
    3. 找出第二组点对之间的所有最短路径。
    4. 把这些最短路径经过的边都打上标记。
    5. 求被打上两次标记的边的边权之和。

    这样会快很多,不过这又是显然有问题的,因为会有边的边权因为出现在多条最短路径中而被重复计算,所以我们不得不牺牲一些时间复杂度,这样改写:

    1. 找出第一组点对之间的所有最短路径。
    2. 把这些最短路径经过的边都打上标记。
    3. 找出第二组点对之间的所有最短路径。
    4. (dfs)求出一条第二组点对之间的最短路径,使得这条路径中已打上标记的路径的边权之和最大。

    嘿,我们把(TLE)的做法和(WA)的做法综合在一起,就得出了(AC)的做法!得出结论(雾):

    [TLE+WA=AC ]

    另外,还有一些小的优化,比如在操作(2)中打标记,我们可以利用(dfs),从起点开始找到一条能到达终点的最短路径,然后把边全部打上标记。我们可以用记忆化来优化这一部分的时间。设变量(to_t[i])表示是否存在一条经过(i)的从(x_1)(y_1)的最短路。若(to_t[i]=1),则有;若(to_t[i]=-1),则无;若(to_t[i]=0),则表示我们还不知道有没有,接着搜索下去才能得到答案:

    int dfs1(int u)
    {
        if(u==t1) return true;//到达终点
        to_t[u]=-1;
        for(int i=top[u];i;i=nex[i])
            if(dis[to[i]]==dis[u]+len[i])//判断是否是最短路
            {
                if(to_t[to[i]]==-1) continue;//走不到终点,溜了
                else if(to_t[to[i]]==1||dfs1(to[i])==1)//走得到或者搜索得到
                {
                    to_t[u]=1;
                    G[u][to[i]]=G[to[i]][u]=len[i];//G数组存被标记上的边权
                }
            }
        return to_t[u];
    }
    

    操作(4)中第二次(dfs)求答案时我们就可以暴力一点,顺着跑就好了。当然,优化方式也有很多,在这里就不举例了。

    //ans[i]记录从起点到i点所能经过的最大被标记边权之和
    void dfs2(int u)
    {
        for(int i=top[u];i;i=nex[i])
            if(dis[to[i]]==dis[u]+len[i]&&ans[to[i]]<G[u][to[i]]+ans[u])//是最短路且对答案有贡献
            {
                ans[to[i]]=G[u][to[i]]+ans[u];//做记录
                dfs2(to[i]);//往下搜索
            }
    }
    

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1505;
    int n,m,s1,t1,s2,t2,dis[MAXN],ans[MAXN],G[MAXN][MAXN],to_t[MAXN];
    int cnt,top[MAXN],to[MAXN*MAXN],len[MAXN*MAXN],nex[MAXN*MAXN];
    bool vis[MAXN];
    int read()
    {
        int re=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
        return re;
    }
    void SPFA(int s)
    {
        memset(dis,0x3f,sizeof dis);
        dis[s]=0;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty())
        {
            int now=Q.front();Q.pop();
            vis[now]=false;
            for(int i=top[now];i;i=nex[i])
                if(dis[to[i]]>dis[now]+len[i])
                {
                    dis[to[i]]=dis[now]+len[i];
                    if(!vis[to[i]])
                    {
                        vis[to[i]]=true;
                        Q.push(to[i]);
                    }
                }
        }
    }
    int dfs1(int u)
    {
        if(u==t1) return true;
        to_t[u]=-1;
        for(int i=top[u];i;i=nex[i])
            if(dis[to[i]]==dis[u]+len[i])
            {
                if(to_t[to[i]]==-1) continue;
                else if(to_t[to[i]]==1||dfs1(to[i])==1)
                {
                    to_t[u]=1;
                    G[u][to[i]]=G[to[i]][u]=len[i];
                }
            }
        return to_t[u];
    }
    void dfs2(int u)
    {
        for(int i=top[u];i;i=nex[i])
            if(dis[to[i]]==dis[u]+len[i]&&ans[to[i]]<G[u][to[i]]+ans[u])
            {
                ans[to[i]]=G[u][to[i]]+ans[u];
                dfs2(to[i]);
            }
    }
    int main()
    {
        n=read(),m=read(),s1=read(),t1=read(),s2=read(),t2=read();
        while(m--)
        {
            int x=read(),y=read(),z=read();
            to[++cnt]=y,len[cnt]=z,nex[cnt]=top[x],top[x]=cnt;
            to[++cnt]=x,len[cnt]=z,nex[cnt]=top[y],top[y]=cnt;
        }
        SPFA(s1);
        if(dfs1(s1)==-1)
        {
            printf("0");
            return 0;
        }
        SPFA(s2);
        memset(ans,-1,sizeof ans);
        ans[s2]=0;
        dfs2(s2);
        printf("%d",ans[t2]);
        return 0;
    }
    
  • 相关阅读:
    jade反编译,把html编译成jade
    runtime环境下的jade
    jade过滤器
    jade-render-renderFile
    【[USACO17DEC]Standing Out from the Herd】
    bzoj 1396: 识别子串
    bzoj 4327:JSOI2012 玄武密码
    hihocoder 后缀自动机四·重复旋律6
    hihocoder 后缀自动机四·重复旋律7
    【[CTSC2012]熟悉的文章】
  • 原文地址:https://www.cnblogs.com/coder-Uranus/p/9815597.html
Copyright © 2020-2023  润新知