poj 3463 计算最短与次短路径数

PS:可以用于剖析最短路的算法原理
计算最短路与次短路路径数,次短路长度为最短路径+1
方法记录最短路径与次短路径,并判断次短路径与最短路径之差是否唯一
方法一:反向spfa + dfs(剪枝) ===> TLE
方法二:A*搜索(第K短路) ===> MLE
方法三:spfa + 优先队列维护 + 递推 ===> AC
有向图,用 dist[][2] 分别记录最短与次短,同时更新 dp[][2]
每个点有两种状态:最短、次短(第一次被访问、第二次(多次)被访问)
节点处理:
第一次被访问:记录距离,最短状态,次数与前驱相同
第二次被访问:
比较与更新第一次的状态,记录第二种状态
多次(两次以上)被访问:
比较与更新第一次、第二次状态
状态更新,即递推过程;
思路是按照最短路距离递增序列,一层一层递推,用优先队列的用来保持递增序列;
更新第一种状态:
dp[v][0] = dp[u][0];
更新第二种状态:
dp[v][0] = dp[u][0] / dp[u][1]; [0] / [1] 与前驱状态相关:前驱是否为第一次、第二次状态
距离与当前点相等:
dp[v][0] += dp[u][0];
dp[v][1] += dp[u][0] / dp[u][1]; [0] / [1] 与前驱状态相关:前驱是否为第一次、第二次状态
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 2222;
struct Pos{
	int id, dis, sta;
	bool operator <( Pos e )const{
		return e.dis < dis;
	}
	void Get( int a, int b, int c ){ id = a, dis = b, sta = c; }
};
struct Edge{
	int v, w, next;
}edge[maxn*100];
int tot, head[maxn];
bool vis[maxn][2];
int n, r, dist[maxn][2], dp[maxn][2];
priority_queue<Pos>q;
int os;
void init()
{
	tot = 0;
	memset( head, -1, sizeof(head) );
}
inline void add_edge( int u, int v, int w )
{
	edge[tot].v = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot++;
}
int spfa( int src, int des )
{
	while( !q.empty() )q.pop();
	memset( dist, -1, sizeof(dist) );
	memset( dp, 0, sizeof(dp) );
	memset( vis, false, sizeof(vis) );
	int i, v, len;
	Pos now, next;
	now.Get( src, 0, 0 );
	dp[src][0] = 1;	dist[src][0] = 0;
	q.push( now );
	while( !q.empty() )
	{
		now = q.top();	q.pop();
		if( vis[now.id][now.sta] )continue;	
		vis[now.id][now.sta] = true;
		for( i = head[now.id]; i != -1; i = edge[i].next )
		{
			v = edge[i].v;
			len = now.dis + edge[i].w;
			if( dist[v][0] == -1 || len < dist[v][0] )
			{
				if( dist[v][0] != -1 )
				{
					dist[v][1] = dist[v][0];
					dp[v][1] = dp[v][0];
					next.Get( v, dist[v][1], 1 );
					q.push( next );
				}
				dist[v][0] = len;
				dp[v][0] = dp[now.id][now.sta];
				next.Get( v, len, 0 );
				q.push( next );
			}else if( len == dist[v][0] ){
				dp[v][0] += dp[now.id][now.sta];
			}else if( dist[v][1] == -1 || len < dist[v][1] ){
				dist[v][1] = len;
				dp[v][1] = dp[now.id][now.sta];
				next.Get( v, len, 1 );
				q.push( next );
			}else if( len == dist[v][1] ){
				dp[v][1] += dp[now.id][now.sta];
			}
		}
	}
	if( dist[des][1] - dist[des][0] == 1 )return dp[des][0] + dp[des][1];
	return dp[des][0];
}
int main()
{
	int cas, i, j, u, v, w;
	scanf( "%d", &cas );
	while( cas-- )
	{
		scanf( "%d%d", &n, &r );
		init();
		for( i = 1; i <= r; i++ )
		{
			scanf( "%d%d%d", &u, &v, &w );
			add_edge( u, v, w );
		}
		int src, des;
		scanf( "%d%d", &src, &des );
		os = spfa( src, des );
		printf( "%d\n", os );
	}
	return 0;
}

好题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值