poj3613 luogu P2886 [USACO07NOV] Cow Relays G
[USACO07NOV] Cow Relays G
题面翻译
给定一张 \(T\) 条边的无向连通图,求从 \(S\) 到 \(E\) 经过 \(N\) 条边的最短路长度。
输入格式
第一行四个正整数 \(N,T,S,E\) ,意义如题面所示。
接下来 \(T\) 行每行三个正整数 \(w,u,v\) ,分别表示路径的长度,起点和终点。
输出格式
一行一个整数表示图中从 \(S\) 到 \(E\) 经过 \(N\) 条边的最短路长度。
数据范围
对于所有的数据,保证 \(1\le N\le 10^6\),\(2\le T\le 100\)。
所有的边保证 \(1\le u,v\le 1000\),\(1\le w\le 1000\)。
题目描述
For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race using the T (2 ≤ T ≤ 100) cow trails throughout the pasture.
Each trail connects two different intersections (1 ≤ I1i ≤ 1,000; 1 ≤ I2i ≤ 1,000), each of which is the termination for at least two trails. The cows know the lengthi of each trail (1 ≤ lengthi ≤ 1,000), the two intersections the trail connects, and they know that no two intersections are directly connected by two different trails. The trails form a structure known mathematically as a graph.
To run the relay, the N cows position themselves at various intersections (some intersections might have more than one cow). They must position themselves properly so that they can hand off the baton cow-by-cow and end up at the proper finishing place.
Write a program to help position the cows. Find the shortest path that connects the starting intersection (S) and the ending intersection (E) and traverses exactly N cow trails.
给出一张无向连通图,求S到E经过k条边的最短路。
输入格式
* Line 1: Four space-separated integers: N, T, S, and E
* Lines 2..T+1: Line i+1 describes trail i with three space-separated integers: lengthi , I1i , and I2i
输出格式
* Line 1: A single integer that is the shortest distance from intersection S to intersection E that traverses exactly N cow trails.
样例 #1
样例输入 #1
2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9
样例输出 #1
10
边数只有100,但是点数有1000,可以离散化然后用临界矩阵,最多200条边。
但是要怎么样才能计算刚好经过\(n\)条边的最短路呢?
以为我觉得这是一个新的知识点,不知道该怎么说思考过程,我是直接看书的。
其实算是倍增,我们用\(f^k[i][j]\)表示从\(i\) 到\(j\)经过k条路的最短路
当\(k=2时\),\(f^2[i][j]=min(a[i][k]+a[k][j])\)
当\(k=4\)时,\(f^4[i][j]=min(f^2[i][k]+f^2[k][j])\)
以此类推,当\(k=r\)时,\(f^r[i][j]=min(f^{r-c}[i][k]+f^{c}[k][j])\)
这其实是一个类似矩阵乘法的过程,对于每个转移,其实相当于是进行了一次乘法,乘法的两边是两个\(f\)数组,表示是经过几条边的最短路,结果的矩阵就是经过更多条边的最短路。
然后就可以用倍增的思想算出来任意数量的答案了。单次乘法的复杂度是\(O(n^3)\),对于\(n\leq 1e6\),只用进行\(log_2(n)\)次即可。
最终复杂度\(O(T^3log(n))\)
这题为什么能用这种方法?
这其实算是一个folyd的特性了,很容易理解为什么上面说的过程是对的,folyd的这种实现形式就是能够很容易的保证这个过程是对的。
但是这种矩阵乘法的方式,需要满足什么条件才可以使用呢?
这题里体现出来的有一个,子问题的重复性,这个其实有一部分是以为folyd是dp,但是这个子问题的重复性不是对普通的folyd的,folyd的模板是不具有这种特殊的重复性的,具有这个性质的是这个改版,还有就是解决方法和处理范围的重复性。这个folyd的处理给我一种用矩阵乘法优化数列来快速计算数列第n项的感觉,很有意思,也很特殊,因为每次计算是完全重复的方法,不能说是和矩阵乘法的过程毫无关系,只能说是一模一样。希望以后能多做到几道这种题目,很,,有意思,让我有一种耳目一新的感觉。大概是我做题少吧。。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
char c=getchar();int a=0,b=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int n,t,s,e;
int a[205][205],d[205][205],f[205][205],ans[205][205];
int T[101][5],all[205],tot,cnt;
/*struct aa
{
int a[501][501];
aa operator * (const aa &x) const
{
aa c;
memset(c.a,0x3f,sizeof(c.a));
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
c.a[i][j]=min(c.a[i][j],a[i][k]+x.a[k][j]);
}
}
}
return c;
}
};*/
map<int,int> ma;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read(),t=read(),s=read(),e=read();
for(int i=1;i<=t;i++)
{
T[i][3]=read(),T[i][2]=read(),T[i][1]=read();
all[++tot]=T[i][1],all[++tot]=T[i][2];
}
all[++tot]=s,all[++tot]=e;
sort(all+1,all+1+tot);
for(int i=1;i<=tot;i++)
{
if(ma[all[i]]==0)
{
ma[all[i]]=++cnt;
}
}
memset(a,0x3f,sizeof(a));
for(int i=1;i<=t;i++)
{
T[i][1]=ma[T[i][1]],T[i][2]=ma[T[i][2]];
a[T[i][1]][T[i][2]]=a[T[i][2]][T[i][1]]=T[i][3];
}
memcpy(d,a,sizeof(a));
bool flag=0;
if(n%2==1)memcpy(ans,a,sizeof(a)),flag=1;
for(int h=1;h<=log(n)/log(2);h++)
{
memset(f,0x3f,sizeof(f));
for(int k=1;k<=tot;k++)
{
for(int i=1;i<=tot;i++)
{
for(int j=1;j<=tot;j++)
{
f[i][j]=min((ll)d[i][k]+d[k][j],(ll)f[i][j]);
}
}
}
if(((n>>h)&1)==1)
{
if(flag==0)
{
memcpy(ans,f,sizeof(f));
flag=1;
}
else
{
memset(d,0x3f,sizeof(d));
for(int k=1;k<=tot;k++)
{
for(int i=1;i<=tot;i++)
{
for(int j=1;j<=tot;j++)
{
d[i][j]=min((ll)f[i][k]+ans[k][j],(ll)d[i][j]);
}
}
}
memcpy(ans,d,sizeof(d));
}
}
memcpy(d,f,sizeof(f));
}
cout<<ans[ma[s]][ma[e]]<<endl;
return 0;
}
还有一个重载运算符的写法,比我这个写法段一倍
可以去luogu上看题解。
这题还有部分贪心的写法,快很多,是\(O(T^2)\),也是在luogu上有