http://codeforces.com/problemset/problem/1196/F
题目描述:
You are given a connected undirected weighted graph consisting of n vertices and m edges.
You need to print the k-th smallest shortest path in this graph (paths from the vertex to itself are not counted, paths from i to j and from j to i are counted as one).
More formally, if d is the matrix of shortest paths, where di,j is the length of the shortest path between vertices i and j(1≤i<j≤n), then you need to print the k-th element in the sorted array consisting of all di,j, where 1≤i<j≤n.
输入描述:
The first line of the input contains three integers n,m and k (2≤n≤2⋅105, n−1≤m≤min(n(n−1)2,2⋅105), 1≤k≤min(n(n−1)2,400) — the number of vertices in the graph, the number of edges in the graph and the value of k, correspondingly.
Then m lines follow, each containing three integers x, y and w (1≤x,y≤n, 1≤w≤109, x≠y) denoting an edge between vertices x and y of weight w.
It is guaranteed that the given graph is connected (there is a path between any pair of vertices), there are no self-loops (edges connecting the vertex with itself) and multiple edges (for each pair of vertices x and y, there is at most one edge between this pair of vertices in the graph).
输出描述:
Print one integer — the length of the k-th smallest shortest path in the given graph (paths from the vertex to itself are not counted, paths from i to j and from j to i are counted as one).
输入样例:
6 10 5
2 5 1
5 3 9
6 2 2
1 3 1
5 1 8
6 5 10
1 6 5
6 4 6
3 6 2
3 4 5
输出样例:
3
大概思路:
k至多为400,比较小,只有最短的400条边可能有效。
将400条边的端点(至多有800个点)放入vector中,按vector的脚标离散化,Floyd算法求多源最短路,排序得到答案。
还可以优化:有效边建图后可能不连通,各个子图可以独立运算(并查集),会降低Floyd的运行时间。
不优化极限AC代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=2e5+20,K=802;
int pre[N],num[N],ca,b[N];
ll ans[N],dis[K][K];
bool vis[N];
struct node
{
int x,y;
ll v;
node() {}
node(int yy,ll vv)
{
y=yy;
v=vv;
}
bool operator <(const node &oth)const
{
return v<oth.v;
}
} h[N];
vector<int>vec;
vector<node>v[N];
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=0; i<m; i++)
scanf("%d%d%lld",&h[i].x,&h[i].y,&h[i].v);
sort(h,h+m);
int en=min(k,m);
for(int i=0; i<en; i++)//建立有效图,并放入vec离散化
{
int x=h[i].x,y=h[i].y;
ll va=h[i].v;
v[x].push_back(node(y,va));
v[y].push_back(node(x,va));
if(!vis[x])
{
b[x]=vec.size();
vec.push_back(x);
vis[x]=1;
}
if(!vis[y])
{
b[y]=vec.size();
vec.push_back(y);
vis[y]=1;
}
}
int len=vec.size();
for(int i=0; i<len; i++)//初始化dis数组
for(int j=0; j<len; j++)
{
if(i==j)dis[i][i]=0;
else dis[i][j]=1e16;
}
for(int i=0; i<len; i++)//dis数组赋初值
{
int te=vec[i];
int l=v[te].size();
for(int j=0; j<l; j++)
{
int ty=v[te][j].y;
dis[i][b[ty]]=v[te][j].v;
dis[b[ty]][i]=v[te][j].v;
}
}
for(int u=0; u<len; u++)//Floyd
{
for(int i=0; i<len; i++)
{
for(int j=i+1; j<len; j++)
{
if(u==i||u==j)continue;
if(dis[i][j]>dis[i][u]+dis[u][j])
{
dis[i][j]=dis[i][u]+dis[u][j];
dis[j][i]=dis[i][u]+dis[u][j];
}
}
}
}
for(int i=0; i<len; i++)//统计
for(int j=i+1; j<len; j++)
ans[ca++]=dis[i][j];
sort(ans,ans+ca);//排序
printf("%lld\n",ans[k-1]);
return 0;
}
并查集分离子图优化代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=2e5+20,K=402;
int pre[N],num[N],ca,b[N];
ll ans[N],dis[K][K];
vector<int>vec[N];
struct node
{
int x,y;
ll v;
node() {}
node(int yy,int vv)
{
y=yy;
v=vv;
}
bool operator <(const node &oth)const
{
return v<oth.v;
}
} h[N];
vector<node>v[N];
int find(int x)
{
if(pre[x]==x)return x;
return pre[x]=find(pre[x]);
}
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx==fy)return;
if(num[fx]<num[fy])
{
pre[fx]=fy;
num[fy]+=num[fx];
}
else
{
pre[fy]=fx;
num[fx]+=num[fy];
}
return;
}
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=0; i<m; i++)
scanf("%d%d%lld",&h[i].x,&h[i].y,&h[i].v);
sort(h,h+m);
int en=min(k,m);
for(int i=0; i<en; i++)
{
v[h[i].x].push_back(node(h[i].y,h[i].v));
v[h[i].y].push_back(node(h[i].x,h[i].v));
}
for(int i=1; i<=n; i++)//并查集初始化
{
pre[i]=i;
num[i]=1;
}
for(int z=0; z<en; z++)//合并
merge(h[z].x,h[z].y);
for(int i=1; i<=n; i++)//vec[i]存储第i个子图的结点
{
int fi=find(i);
b[i]=vec[fi].size();
vec[fi].push_back(i);
}
for(int z=1; z<=n; z++)//遍历祖父结点
{
int len=vec[z].size();
if(len<=1)continue;//跳过孤立点
for(int i=0; i<len; i++)//初始化
for(int j=0; j<len; j++)
{
if(i==j)dis[i][i]=0;
else dis[i][j]=1e16;
}
for(int i=0; i<len; i++)//初始化
{
int te=vec[z][i];
int l=v[te].size();
for(int j=0; j<l; j++)
{
int ty=v[te][j].y;
dis[i][b[ty]]=v[te][j].v;
dis[b[ty]][i]=v[te][j].v;
}
}
for(int u=0; u<len; u++)//Floyd
{
for(int i=0; i<len; i++)
{
for(int j=i+1; j<len; j++)
{
if(u==i||u==j)continue;
if(dis[i][j]>dis[i][u]+dis[u][j])
{
dis[i][j]=dis[i][u]+dis[u][j];
dis[j][i]=dis[i][u]+dis[u][j];
}
}
}
}
for(int i=0; i<len; i++)//统计
for(int j=i+1; j<len; j++)
ans[ca++]=dis[i][j];
}
sort(ans,ans+ca);//排序
printf("%lld\n",ans[k-1]);
return 0;
}