zoukankan      html  css  js  c++  java
  • P1119 灾后重建 题解

    前置芝士:

    我的Floyd基础知识讲解


    说白了,Floyd本质上就是一个dp:

    K为外层状态,i和j为内层枚举。Floyd完整状态其实是:(f[k][i][j])表示以前K个点为转移点(即编号为1~k的点可以被用来松弛),从(i)(j)的最短路径。

    每条最短路径都是以k为基础的,对于未枚举进k的点将不被考虑进最短路径的松弛,k也就作为整个图的最短路径的前提条件,那么自然作为最外层状态转移。

    考虑这道题:

    给你一张无向图,给你每一个节点的信息t表示当前节点在时间为t以及以后可以到达。

    翻译一下,也就是可以考虑进最短路的松弛。

    朴素想法,对于每个t,我们可以枚举每一个结点,如果结点的(t>)当前询问的时间,将不被考虑进图中。每一次询问在重新建完图后跑一边Floyd,并在判断起点村庄s到终点l能否到达,如果能到达输出即可。

    很明显,T的死死的。每次跑一遍floyd实在是太浪费了时间,而且题目中的数据是递增的。出题人给你的优化条件你怎么能不用呢。

    k表示考虑了1~n的节点作为可松弛点,也就是说:编号1到n的节点都能够到达。

    换句话说,1到n节点满足了被加入最短路集合的条件。

    对于这道题,从暴力思路可以看出,一个节点被加入最短路集合的条件就是当前节点的t值小于等于询问的时间t。而出题人给你的t是单调的。那么,我们设上一次询问的时间为(t_{0}),这一次询问的时间为(t_{1}),以t为floyd最外层状态k,对于(t_{0})之前的k我们就不必再跑一遍了,从上一次能够到达的编号为now的节点开始往后判断节点的t是否小于询问的t,直到大于询问的t为止。

    原因很好理解,节点的t随着编号的上升而上升,而询问的t又上升,那么考虑过的节点一定满足加入最短路集合的条件,也就不用再跑一遍Floyd((n^2))的部分了。

    核心代码:

    q=read();
    	while(q--)
    	{
    		u=read(),v=read(),w=read();
    		while(now<n&&a[now]<=w)
    		{
    			dp(now);//now节点就可以被加入可用来松弛的节点的集合,跑一边floyd
    			now++;//考虑编号更大的,t值更大的结点。
    		}
    	}
    	\Floyd部分
    inline void dp(int now)
    {
    	for(int i=0;i<n;i++)
    		for(int j=0;j<n;j++)
    		{
    			dis[i][j]=min(dis[i][j],dis[i][now]+dis[now][j]);
    		}
    }
    

    全部代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define re register
    using namespace std;
    const int N=201;
    inline int read()
    {
    	int x=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int n,m,a[N],u,w,v,q,judge=-1,dis[N][N],now;
    inline void add(int from,int to,int w)
    {
    	dis[from][to]=dis[to][from]=w;
    }
    inline void dp(int now)
    {
    	for(int i=0;i<n;i++)
    		for(int j=0;j<n;j++)
    		{
    			dis[i][j]=min(dis[i][j],dis[i][now]+dis[now][j]);
    		}
    }
    int main()
    {
    	n=read(),m=read();
    	for(int i=0;i<n;i++)
    		for(int j=0;j<n;j++)
    			dis[i][j]=1e9+7;
    	for(int i=0;i<n;i++)
    	{
    		a[i]=read();
    	}
    	for(int i=1;i<=m;i++)
    	{
    		u=read(),v=read(),w=read();
    		add(u,v,w);
    	}
    	q=read();
    	while(q--)
    	{
    		u=read(),v=read(),w=read();
    		while(now<n&&a[now]<=w)
    		{
    			dp(now);
    			now++;
    		}
    		if(a[u]>w||a[v]>w)printf("%d
    ",judge);
    			else
    			{
    				if(dis[u][v]==1e9+7)printf("%d
    ",judge);
    				else printf("%d
    ",dis[u][v]);
    			}
    	}
    	return 0;
    }
    
    

    希望看完这篇博文后能够加深各位对Floyd的理解!

    完结撒花

  • 相关阅读:
    P4141 消失之物(退背包模板)
    P5829 【模板】失配树
    P4827 [国家集训队] Crash 的文明世界
    P4074 [WC2013]糖果公园
    P3242 [HNOI2015]接水果
    P2371 [国家集训队]墨墨的等式
    P4819 [中山市选]杀人游戏
    P5331 [SNOI2019]通信
    BZOJ1082 [SCOI2005]栅栏
    poj1475 Pushing Boxes[双重BFS(毒瘤搜索题)]
  • 原文地址:https://www.cnblogs.com/lbssxz/p/13357239.html
Copyright © 2011-2022 走看看