[BZOJ1706]:[usaco2007 Nov]relays 奶牛接力跑

题目传送门


题目描述

FJN头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点自然是在牧场中现有的T条跑道上。农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点${I1}_i$${I2}_i$。每个交汇点都是至少两条跑道的端点。奶牛们知道每条跑道的长度${length}_i$,以及每条跑道连结的交汇点的编号并且,没有哪两个交汇点由两条不同的跑道直接相连。你可以认为这些交汇点和跑道构成了一张图。为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道。


输入格式

1行:4个用空格隔开的整数:NTS,以及E
2...T+1行:第i+13个以空格隔开的整数:${length}_i$${I1}_i$,以及${I2}_i$,描述了第i条跑道。


输出格式

1行:输出1个正整数,表示起点为S、终点为E,并且恰好经过N条跑道的路径的最小长度。


样例

样例输入:

2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9

样例输出:

10


数据范围与提示

2≤N1,000,000

2T100

1${I1}_i$1,0001${I2}_i$1,0001${length}_i$1,000


题解

根据矩阵乘法的性质,单位矩阵每乘一次转移矩阵就相当与走了一步,故考虑矩阵乘法。

我们可以发现计算最短路径的Floyd算法结构和矩阵乘法是类似的,但是内部的加法与乘法变为了取最小值与加法。

可以发现不管是加法乘法还是取最小值,其都满足矩阵乘法要求的结合律与分配律, 所以我们可以更改矩阵乘法的定义,利用邻接矩阵做快速幂即可。

1≤${I1}_i$≤1,000,1≤${I2}_i≤1,000$显然是不能接受的,但是2≤T≤100令人舒爽,所以需要离散化一手即可。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int ans[1001][1001],Map[1001][1001],flag[1001][1001];
int lsh[1001],cnt;
void _doudou()//单位矩阵乘转移矩阵
{
    for(int i=1;i<=cnt;i++)
    	for(int j=1;j<=cnt;j++)
    		flag[i][j]=ans[i][j],ans[i][j]=0;
	memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=cnt;i++)
    	for(int j=1;j<=cnt;j++)
    		for(int k=1;k<=cnt;k++)
    			ans[i][j]=min(ans[i][j],flag[i][k]+Map[k][j]);//记得是min
}
void _wzc()//转移矩阵自乘
{
    for(int i=1;i<=cnt;i++)
    	for(int j=1;j<=cnt;j++)
    		flag[i][j]=Map[i][j],Map[i][j]=0;
	memset(Map,0x3f,sizeof(Map));
    for(int i=1;i<=cnt;i++)
    	for(int j=1;j<=cnt;j++)
    		for(int k=1;k<=cnt;k++)
    			Map[i][j]=min(Map[i][j],flag[i][k]+flag[k][j]);
}
void pre_work()//初始化
{
	memset(Map,0x3f,sizeof(Map));
	memset(ans,0x3f,sizeof(ans));
	for(int i=1;i<=1000;i++)
		ans[i][i]=0;
}
int main()
{
	pre_work();
	int n,m,s,t;
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++)
	{
		int x,y,v;
		scanf("%d%d%d",&v,&x,&y);
		if(!lsh[x])lsh[x]=++cnt;
		if(!lsh[y])lsh[y]=++cnt;
		Map[lsh[x]][lsh[y]]=Map[lsh[y]][lsh[x]]=v;
	}
    while(n)//快速幂
    {
        if(n&1)_doudou();
        _wzc();
        n>>=1;
    }
    cout<<ans[lsh[s]][lsh[t]];
	return 0;
}

rp++

posted @ 2019-07-18 11:46  HEOI-动动  阅读(216)  评论(0编辑  收藏  举报