欢迎来到lbxer的博客
lbxer
lbxer
晓看天色暮看云,行也思君,坐也思君

csp赛前刷题篇 图论篇 [USACO07NOV]Cow Relays G

https://www.luogu.com.cn/problem/P2886

看一眼本题,Floyd算法。那么什么是Floyd算法呢??

  Floyd

Floyd是一种最短路算法,适用于点数较少的图

Floyd的本质是动态规划,它的状态定义以及转移:

f[i][j]f[i][j]为ii到jj的最短距离

f[i][j]=min(f[i][j],f[i][k]+f[k][j])f[i][j]=min(f[i][j],f[i][k]+f[k][j])

代码实现:

	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

下面让我们魔改一下
令f[k,i,j]表示 从i经过k条边到达j 原Floyd表达 从i 到j走1~k个点,最短路径是多少。
魔改后 转移方程 f[a+b,i,j]=min(f[a,i,k],f[b,i,k]),k=1~n。
d[a,i,k] 中的 k不是什么第 k 条边,而是表示从 i 走了 a 条边(就是相当于走了a步)以后到达的结点 k ,因此这个 k 是在 1 ~ N 之间的一个点。
这个转移方程表示从 i 到 j 走 a+b (x)条边,等于在所有从 i 走 a 条边到 k ,再从 k 走 b (xa) 条边到 j 中取最小值。
这样我们要求的是经过 N 条边的最短路,最开始输入的每一个都是1条边(经过一条边的两个点的距离),那么我们要得到经过N条边就可以按照上述的方程,加 N-1 次 (d[2,i,j] = d[1,i,k] + d[1,k,j])
同时,这里可以用快速幂来优化。
这里有一百条边,用两百个点,给到1000.所以要离散化一下。
离散化
即在不i改变数据相对大小的前提下,对数据进行相应的缩小。通常使用STL算法离散化操作。
步骤为:排序,去重,lower bount。
代码如下:

   sort(sub_a,sub_a+n);

   int size=unique(sub_a,sub_a+n)-sub_a;//size为离散化后元素个数

   for(i=0;i<n;i++) a[i]=lower_bound(sub_a,sub_a+size,a[i])-sub_a + 1;//k为b[i]经离散化后对应的值

本题代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>

#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;

const int N =  507;

int k,n,m,S,E;
int g[N][N];
int d[N][N];

void mul(int c[][N],int a[][N],int b[][N]){
    static int temp[N][N];
    memset(temp,0x3f,sizeof temp);
    for(int k = 1;k <= n;++k)
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                temp[i][j] = min(temp[i][j],a[i][k] + b[k][j]);
    memcpy(c,temp,sizeof temp);
}

void qpow(){
    memset(d,0x3f,sizeof d);
    for(int i = 1;i <= n;++i)
        d[i][i] = 0;
    while(k){
        if(k & 1)mul(d, d, g);//d = d * g;
        mul(g, g, g);//g = g * g;
        k >>= 1;
    }
}

int main(){
    cin>>k>>m>>S>>E;
    memset(g,0x3f,sizeof g);
    //这里g[i][i]不能置为0,因为这里g[i][j]表示的是从i走1条边到达j,i不能经过1条边到i,因此应该也是INF
    map<int,int>ids;
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&c,&a,&b);
        if(!ids.count(a))ids[a] = ++n;
        if(!ids.count(b))ids[b] = ++n;
        a = ids[a],b = ids[b];

        g[a][b] = g[b][a] = min(g[a][b],c);
    }
    if(!ids[S])ids[S] = ++n;
    if(!ids[E])ids[E] = ++n;
    S = ids[S],E = ids[E];

    qpow();

    cout<<d[S][E]<<endl;
    return 0;

}
View Code

 


   
posted @ 2020-10-11 18:30  雷痕小祥  阅读(185)  评论(0编辑  收藏  举报
Live2D