CodeForces-1196F-K-th Path-贪心建图

10 篇文章 0 订阅
7 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值