POJ 2449 Remmarguts' Date(k短路)

190 篇文章 2 订阅
15 篇文章 0 订阅

题面

题意

给出一幅有向图,求出其中的第k短路

方法

首先应该想到用BFS加上优先队列,按照距离由短到长进行搜索,当到达终点次数达到k次时停止.

代码(错误)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define P pair<ll,ll>
#define fi first
#define se second
#define N 1010
#define M 100100
using namespace std;

struct Bn
{
	ll next,to,time;
}bn[M];
ll n,m,k,first[N],bb,s,t;
priority_queue<P,vector<P>,greater<P> >pq;
P tmp;

void add(ll u,ll v,ll w)
{
	bb++;
	bn[bb].to=v;
	bn[bb].next=first[u];
	bn[bb].time=w;
	first[u]=bb;
}

int main()
{
	ll i,j,p,q,o;
	P now;
	memset(first,-1,sizeof(first));
	for(;!pq.empty();pq.pop());
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",&p,&q,&o);
		add(p,q,o);
	}
	cin>>s>>t>>k;
	tmp.fi=0;
	tmp.se=s;
	pq.push(tmp);
	for(;!pq.empty();)
	{
		tmp=pq.top();
		pq.pop();
		if(tmp.se==t)
		{
			if(k==1)
			{
				printf("%lld",tmp.first);
				return 0;
			}
			k--;
		}
		now=tmp;
		p=first[now.se];
		for(;p!=-1;p=bn[p].next)
		{
			tmp.fi=now.fi+bn[p].time;
			tmp.se=bn[p].to;
			pq.push(tmp);
		}
	}
	printf("-1");
	return 0;
}

这种方法有以下缺点:
1.上述思路占用空间过大,会爆队列
2.当遇到无法到达终点时的情况就会陷入死循环
我们先解决第二个问题
要求k短路,则每个点最多扫过k次,因而我们可以记录每个点走过的次数,若超过k则跳过这个点,则不仅可解决上述问题,还能起到剪枝作用

对于第一个问题则需要改变排序的方式,因为目标是t,则可以根据到达t的最短需要长度进行排序,而不是根据已经经过路径,若根据已经经过路径长度排序,则可能会多次搜索离t较远的点,浪费大量空间.
为了计算出到达t的最短路,可以以t为起点,用迪杰斯特拉算出每一点到达它的最小距离(因点数少,边数多,可以用邻接矩阵),重定义一下小于号即可.

另外还要注意当起点和终点重合时,应该让k+1,因为一开始到达的那一次不算

代码(正解)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 2010
#define M 200200
#define INF 0x3f3f3f3f
using namespace std;

int n,m,k,first[N],bb,s,t,cnt[N],d[N],mm[N][N];
struct Bn
{
	int next,to,time;
} bn[M];
struct Dn
{
	int len,pos;
	bool operator < (const Dn &u) const
	{
		return len + d[pos] > u.len + d[u.pos];
	}
};
priority_queue<Dn>pq;
bool vis[N];
Dn tmp,now;

inline void add(int u,int v,int w)
{
	bb++;
	bn[bb].to=v;
	bn[bb].next=first[u];
	bn[bb].time=w;
	first[u]=bb;
}

inline void gx(int u)
{
	int i;
	for(i=1; i<=n; i++)
	{
		if(vis[i]) continue;
		if(d[i]>mm[i][u]+d[u])
		{
			d[i]=mm[i][u]+d[u];
		}
	}
}

inline void get()
{
	int i,j,l,mn;
	d[t]=0;
	vis[t]=1;
	gx(t);
	for(i=1; i<=n; i++)
	{
		mn=INF;
		l=0;
		for(j=1; j<=n; j++)
		{
			if(vis[j]) continue;
			if(d[j]<mn)
			{
				l=j;
				mn=d[j];
			}
		}
		if(!l) return;
		vis[l]=1;
		gx(l);
	}
}

int main()
{
//	freopen("1.txt","r",stdin);
	int i,j,p,q,o;
	memset(first,-1,sizeof(first));
	memset(d,INF,sizeof(d));
	memset(mm,INF,sizeof(mm));
	for(;!pq.empty();pq.pop());
	scanf("%d%d",&n,&m);
	for(i=1; i<=m; i++)
	{
		scanf("%d%d%d",&p,&q,&o);
		add(p,q,o);
		mm[p][q]=min(mm[p][q],o);
	}
	scanf("%d%d%d",&s,&t,&k);
	k+= s==t;
	get();

	if(!vis[s])
	{
		printf("-1\n");
		return 0;
	}

	tmp.pos=s;
	tmp.len=0;
	pq.push(tmp);
	for(; !pq.empty();)
	{
		now=pq.top();
		pq.pop();
		cnt[now.pos]++;
		if(cnt[t]==k)
		{
			printf("%d\n",now.len);
			return 0;
		}
		if(cnt[now.pos]>k) continue;
		for(p=first[now.pos] ; p!=-1; p=bn[p].next)
		{
			tmp.len=now.len+bn[p].time;
			tmp.pos=bn[p].to;
			pq.push(tmp);
		}
	}
	printf("-1\n");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值