我是靠谱客的博主 高兴大树,最近开发中收集的这篇文章主要介绍【poj3613】Cow Relays,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

【题意】

给定一个含T条边的无向图,求从s到e的恰好经过n条边的最短路(可以经过重复边)。

【题解】

我们先来回想一下floyd。它的本质实际上是DP

f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]为从i开始,经过1~k的某些点到达j的最短路的总权值,则:
f [ k ] [ i ] [ j ] = m i n { f [ k − 1 ] [ i ] [ k ] + f [ k − 1 ] [ k ] [ j ] } f[k][i][j]=min{f[k-1][i][k]+f[k-1][k][j]} f[k][i][j]=min{f[k1][i][k]+f[k1][k][j]}
降维可得:
f [ i ] [ j ] = m i n { f [ i ] [ k ] + f [ k ] [ j ] } f[i][j]=min{f[i][k]+f[k][j]} f[i][j]=min{f[i][k]+f[k][j]}
同时,这也是为什么要先枚举k的原因。

那么,按照这个思路,我们将它改一改。设 f [ t ] [ i ] [ j ] f[t][i][j] f[t][i][j]为从 i i i j j j经过 t t t个中间点的最短路, G [ i ] [ j ] G[i][j] G[i][j]为图初始的邻接矩阵。则:
f [ t ] [ i ] [ j ] = m i n { f [ t − 1 ] [ i ] [ k ] + g [ k ] [ j ] } f[t][i][j]=min{f[t-1][i][k]+g[k][j]} f[t][i][j]=min{f[t1][i][k]+g[k][j]}
目测要T。接下来考虑优化。
我们先把 f f f的第一维去掉,而是用多个数组来表示不同状态下的 f [ i ] [ j ] f[i][j] f[i][j],记作 d t [ i ] [ j ] d_t[i][j] dt[i][j]。则原方程可转化为:
d t [ i ] [ j ] = m i n { d t − 1 [ i ] [ k ] + g [ k ] [ j ] } d_t[i][j]=min{d_{t-1}[i][k]+g[k][j]} dt[i][j]=min{dt1[i][k]+g[k][j]}
再想一想,我们还可以把它改写成下列形式:
d t [ i ] [ j ] = m i n { d t ′ [ i ] [ k ] + d t − t ′ [ k ] [ j ] } d_t[i][j]=min{d_{t'}[i][k]+d_{t-t'}[k][j]} dt[i][j]=min{dt[i][k]+dtt[k][j]}
我们发现,其实这个方程的转移是满足结合律的。所以,在优化的时候,我们可以像矩阵快速幂一样,对方程的转移进行二进制优化。
仿照矩阵快速幂,我们可以像这样定义新乘法:

#define ll long long
struct matrix{
	ll a[max_n][max_n];
	matrix(){memset(a, 0x3f, sizeof a);}
	matrix operator *(const matrix b) const
	{
		matrix c;
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++)
				for(int k = 1; k <= n; k++)
					c.a[i][j] = min(c.a[i][j], a[i][k] + b.a[k][j];
		return c;
	}
};

在这篇博客里提到:

G n [ i ] [ j ] G^n[i][j] Gn[i][j]表示从i走n步到j的方案数

于是,我们对原邻接矩阵做n次上面的奇怪的乘法,就可以得到答案。
由于边数顶破天100条而编号可能很大,所以我们可以对点的编号离散化。

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mn = 205, mm = 1005;
int cnt;
struct matrix{
    int a[mn][mn];
    matrix() {memset(a, 0x3f, sizeof a);}
    matrix operator* (const matrix x) const
    {
        matrix c;
        for(int i = 1; i <= cnt; i++)
            for(int j = 1; j <= cnt; j++)
                for(int k = 1; k <= cnt; k++)
                    c.a[i][j] = min(c.a[i][j], a[i][k] + x.a[k][j]);
        return c;
    }
}ret, h;
struct edge{
    int a, b, w;
}e[mm];
bool vis[mm];
int val[mm];
inline void ksm(int m)
{
    while(m)
    {
        if(m & 1)
            ret = ret * h;
        h = h * h, m >>= 1;
    }
}
int main()
{
    int n, m, s, t, i;
    scanf("%d%d%d%d", &m, &n, &s, &t);
    for(i = 1; i <= n; i++)
        scanf("%d%d%d", &e[i].w, &e[i].a, &e[i].b), vis[e[i].a] = vis[e[i].b] = 1;
    for(i = 1; i <= 1000; i++)
        if(vis[i])
            val[i] = ++cnt;
    for(i = 1; i <= n; i++)
    {
        int a = val[e[i].a], b = val[e[i].b];
        h.a[a][b] = h.a[b][a] = e[i].w;
    }
    for(i = 1; i <= cnt; i++)
        ret.a[i][i] = 0;	//这里最开始的矩阵的0次方表示从i至j走0步的最短路,所以除了ret.a[i][i]=0外全部取正无穷
    ksm(m);
    printf("%dn", ret.a[val[s]][val[t]]);
}

最后

以上就是高兴大树为你收集整理的【poj3613】Cow Relays的全部内容,希望文章能够帮你解决【poj3613】Cow Relays所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部