2 最短路
short.in/.out/.cpp
2.1 问题描述
给一张n 个点,m 条边的有向图。
每个点有点权vi, 每条边有边权wi。
询问q 次, 每次询问两个点从u 到v 的最短路。
最短路的定义是所有从u 到v 路径的权值的最小值。
一条从u 到v 路径的权值为路径上的边权和+ 路径经过点的最大点权
值(包括u,v)。
若没有从u 到v 的路径, 输出-1。
2.2 输入格式
第一行包含三个整数n,m,q。
接下来n 个正整数表示vi。
接下来m 行, 每行三个正整数a,b,w, 表示有一条从a 到b 权值为w 的
单向边。
接下来q 行, 每行两个数u,v, 表示询问从u 到v 的最短路。
2.3 输出格式
共q 行, 每行一个整数, 表示询问的答案。
2.4 样例输入
4 4 2
1 2 5 8
1 2 2
2 3 3
1 4 1
4 3 1
1 3
2 4
4
2.5 样例输出
10
-1
2.6 数据规模及约定
对于10% 的数据, 满足n5;
对于30% 的数据, 满足n10;
对于另外20% 的数据, 满足若存在(u,v) 则存在(v,
u) 并且wi 相等;
对于60% 的数据, 满足n100;
short.in/.out/.cpp
2.1 问题描述
给一张n 个点,m 条边的有向图。
每个点有点权vi, 每条边有边权wi。
询问q 次, 每次询问两个点从u 到v 的最短路。
最短路的定义是所有从u 到v 路径的权值的最小值。
一条从u 到v 路径的权值为路径上的边权和+ 路径经过点的最大点权
值(包括u,v)。
若没有从u 到v 的路径, 输出-1。
2.2 输入格式
第一行包含三个整数n,m,q。
接下来n 个正整数表示vi。
接下来m 行, 每行三个正整数a,b,w, 表示有一条从a 到b 权值为w 的
单向边。
接下来q 行, 每行两个数u,v, 表示询问从u 到v 的最短路。
2.3 输出格式
共q 行, 每行一个整数, 表示询问的答案。
2.4 样例输入
4 4 2
1 2 5 8
1 2 2
2 3 3
1 4 1
4 3 1
1 3
2 4
4
2.5 样例输出
10
-1
2.6 数据规模及约定
对于10% 的数据, 满足n5;
对于30% 的数据, 满足n10;
对于另外20% 的数据, 满足若存在(u,v) 则存在(v,
#include <cstdio>
#include <cmath>
#include <ctime>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include<map>
#include<list>
#define pb push_back
#define lb lower_bound
#define sqr(x) (x)*(x)
#define lowbit(x) (x)&(-x)
#define Abs(x) ((x) > 0 ? (x) : (-(x)))
#define forup(i,a,b) for(int i=(a);i<=(b);i++)
#define fordown(i,a,b) for(int i=(a);i>=(b);i--)
#define ls(a,b) (((a)+(b)) << 1)
#define rs(a,b) (((a)+(b)) >> 1)
#define getlc(a) ch[(a)][0]
#define getrc(a) ch[(a)][1]
#define maxn 405
#define maxm 100005
#define INF 1070000000
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template<class T> inline
void read(T& num){
num = 0; bool f = true;char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = false;ch = getchar();}
while(ch >= '0' && ch <= '9') {num = num * 10 + ch - '0';ch = getchar();}
num = f ? num: -num;
}
int out[100];
template<class T> inline
void write(T x,char ch){
if (x==0) {putchar('0'); putchar(ch); return;}
if (x<0) {putchar('-'); x=-x;}
int num=0;
while (x){ out[num++]=(x%10); x=x/10;}
fordown(i,num-1,0) putchar(out[i]+'0'); putchar(ch);
}
/*========================================================*/
struct Node
{int v,id;};Node node[maxn];
int n,m,q;
int v[maxn],dis[maxn][maxn],g[maxn][maxn];
int maxnode[maxn][maxn];
int num[maxn];
bool cmp(Node x,Node y)
{ return x.v<y.v;}
void init()
{read(n); read(m);read(q);
forup(i,1,n)
{ read(node[i].v);node[i].id=i;}
forup(i,1,m)
{int x,y,z; read(x);read(y);read(z);
dis[x][y]=min(dis[x][y],z);
}
}
void solve()
{
sort(node+1,node+n+1,cmp);
forup(i,1,n)
forup(j,1,n)
{ int x=node[i].id; int y=node[j].id;
maxnode[x][y]=max(node[i].v,node[j].v);
dis[x][y]=dis[x][y]+maxnode[x][y];
}
forup(k,1,n)
forup(i,1,n)
forup(j,1,n)
{ int x=node[k].id,y=node[i].id,z=node[j].id;
if(dis[y][z]>dis[y][x]-maxnode[y][x]+dis[x][z]-maxnode[x][z]+max(maxnode[y][x],maxnode[x][z]))
{ dis[y][z]=dis[y][x]-maxnode[y][x]+dis[x][z]-maxnode[x][z]+max(maxnode[y][x],maxnode[x][z]);
maxnode[y][z]=max(maxnode[y][x],maxnode[x][z]);
}
}
}
int main()
{
freopen("short.in","r",stdin);
freopen("short.out","w",stdout);
forup(i,1,400)
forup(j,1,400)
{ dis[i][j]=INF;}
init();
solve();
forup(i,1,n) num[node[i].id]=node[i].v;
forup(i,1,q)
{int x,y; read(x);read(y);
if(x==y)
{write(num[x],'\n');continue;}
if(dis[x][y]>=INF/3)
{ write(-1,'\n');
}
else
{ write(dis[x][y],'\n');
}
}
return 0;
}
u) 并且wi 相等;
对于60% 的数据, 满足n100;
对于100% 的数据, 满足n400, m n2,
n 400,多源最短路。那么弗洛伊德无疑,但是裸的弗洛伊德肯定是不对的,举个比较。草率地例子,两两合并的时候,其中一边的maxnode非常大,这时可以不用去管另一边的maxnode,那么结果显然就会有和原来暴力佛洛依德偏差,那么怎么办呢。我们已经注意到似乎和最大点有关系了。那么就设法给循环顺序排个序,点权大的排前面(事后发现其实只要外层的k)排序就好了。自己造了几个特殊样例发现能过,然后考试的时候就这么水掉了。。。
事后开始想证明,大约要从弗洛伊德的思想开始,大家都知道佛洛依德是个类似于dp的东西,最外层的k是阶段,可以形象的理解为中间点。那么k递增有什么好处呢。那么显然前面的中间点对后面的就没有影响了。那么貌似就可以直接做了(话说这么胡扯真的算证明吗。。。)