[BZOJ1706]:[usaco2007 Nov]relays 奶牛接力跑
题目传送门
题目描述
FJ的N头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点自然是在牧场中现有的T条跑道上。农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点${I1}_i$和${I2}_i$。每个交汇点都是至少两条跑道的端点。奶牛们知道每条跑道的长度${length}_i$,以及每条跑道连结的交汇点的编号并且,没有哪两个交汇点由两条不同的跑道直接相连。你可以认为这些交汇点和跑道构成了一张图。为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道。
输入格式
第1行:4个用空格隔开的整数:N,T,S,以及E。
第2...T+1行:第i+1为3个以空格隔开的整数:${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≤N≤1,000,000。
2≤T≤100。
1≤${I1}_i$≤1,000,1≤${I2}_i$≤1,000,1≤${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++