CodeForces - 1196F K-th Path(最短路+思维)好题

题目链接:点击查看

题目大意:给出一个 n 个点,m 条边的无向图,需要求出图中第 k 短的路径

题目分析:k 是 400,本来以为是需要思考 k * n 或 k * m 的算法,搞了半天最后原来是 k^3 的算法。。

首先考虑,先把图中前 k 小的边全部拿出来,比较显然的是,答案最坏也不可能超过第 k 小的这条边

然后对于这 k 条边涉及到的 2 * k 个点单独拎出来跑弗洛伊德,然后将 k * ( k - 1 ) / 2 条路径的权值排个序求第 k 小就是答案了

妙啊

2020.12.3UPDATE:

用bfs也可以做,昨天想到用bfs了,只不过不会对路径判重,今天看题解受到启发,可以将无向边拆成两条有向边,然后求第 2 * k 小的路径即是答案

兴致勃勃写了一发 n * k * logn 的算法,交上去 MLE 了,说明暴力扩展是不太合适的,所以考虑优化

可以将邻接表中的所有邻边按照权值排序,并且对于每条边来说,之前记录的是 ( st , ed ),现在我们需要记录一个中间点 x,也就是 ( st , x , ed ),满足最后一条边是 x -> ed,这样还看不出来有什么好处,但是我们实际上记录的是 ( st , x , index ),index 指的是 x 的邻接表中的第 index 个元素,自然可以表示出 ed 了,并且还有一个最大的好处就是,在扩展的时候,将 st -> x -> index 这条边撤回,改成 st -> x -> index+1 一定是最优的,所以扩展状态的时候就有两种途径了:

  1. ( st , x , index )扩展到(st , x , index + 1 )
  2. ( st , x , index )扩展到(st , ed , 0),这里的 ed 就是 x 的第 index 个元素表示的点

因为每次只扩展两个状态,所以 bfs 的复杂度是 O( n ) ,因为需要对每条边排序,所以总时间复杂度是 O( nlogn ) 的

代码:
Floyd:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const LL inf=0x3f3f3f3f3f3f3f3f;

const int N=1e3+100;

struct Edge
{
	int u,v,w;
	void input()
	{
		scanf("%d%d%d",&u,&v,&w);
	}
	bool operator<(const Edge& t)const
	{
		return w<t.w;
	}
}edge[N*N];

vector<int>node;

LL maze[N][N];

void floyd(int n)
{
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				maze[i][j]=min(maze[i][j],maze[i][k]+maze[k][j]);
}

int get_id(int x)
{
	return lower_bound(node.begin(),node.end(),x)-node.begin()+1;
}

void discreate()
{
	sort(node.begin(),node.end());
	node.erase(unique(node.begin(),node.end()),node.end());
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.ans.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
		edge[i].input();
	sort(edge+1,edge+1+m);
	for(int i=1;i<=k;i++)
	{
		node.push_back(edge[i].u);
		node.push_back(edge[i].v);
	}
	discreate();
	n=node.size();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			maze[i][j]=i==j?0:inf;
	for(int i=1;i<=k;i++)
	{
		int u=get_id(edge[i].u),v=get_id(edge[i].v);
		maze[u][v]=maze[v][u]=edge[i].w;
	}
	floyd(n);
	vector<LL>ans;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			ans.push_back(maze[i][j]);
	nth_element(ans.begin(),ans.begin()+k-1,ans.end());
	printf("%lld\n",ans[k-1]);













    return 0;
}

bfs:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e6+100;

struct Node
{
	int st,ed,id;
	LL w;
	bool operator<(const Node& t)const
	{
		return w>t.w;
	}
};

set<pair<int,int>>vis;//st,ed

vector<pair<int,int>>node[N];

priority_queue<Node>q;

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.ans.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	while(m--)
	{
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		node[u].emplace_back(w,v);
		node[v].emplace_back(w,u);
	}
	for(int i=1;i<=n;i++)
	{
		sort(node[i].begin(),node[i].end());
		q.push({i,i,0,node[i][0].first});
		vis.emplace(i,i);
	}
	k*=2;
	while(q.size())
	{
		auto cur=q.top();
		q.pop();
		if(cur.id+1<node[cur.ed].size())
			q.push({cur.st,cur.ed,cur.id+1,cur.w-node[cur.ed][cur.id].first+node[cur.ed][cur.id+1].first});
		if(vis.count(make_pair(cur.st,node[cur.ed][cur.id].second)))
			continue;
		vis.emplace(cur.st,node[cur.ed][cur.id].second);
		if(--k==0)
			return 0*printf("%lld\n",cur.w);
		q.push({cur.st,node[cur.ed][cur.id].second,0,cur.w+node[node[cur.ed][cur.id].second][0].first});
	}









    return 0;
}

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frozen_Guardian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值