HDU 3191 How Many Paths Are There

传送门

次短路条数。
一个有向图,给定起点终点,问你次短路条数,保证有次短路,保证没有环路。

按理说这道题没什么可说的,但是,这道题有一个很大的坑,就是会有权值为零的边。这是我看了这道题的Discuss才知道的,感谢。

求条数的问题对零环敏感,这我们知道,但是这道题说了保证没有环路,所以本来不用担心。但是,求条数对零边也敏感(不管你是求最短路条数还是第k短路条数)。为什么?

问题出在else if (!vis[0][n] && mind + w == d[0][n])else if (!vis[1][n] && mind + w == d[1][n])这两个情况下。假设当前边的w为零,而且当前枚举的中介点mind等于d[][n],那么你必须保证当前中介点u一定能更新到n,也就是说现在vis[][n]一定还是false
然而,两个相等的d值在优先队列里出队的次序是未知的。

你可能会想到删去!vis[][]的判断。但是,没有用,依然WA。因为即使那个n点的num正确了,但是n点先出队访问它自己的邻接点了,它已经把自己错误的num值传递出去了。于事无补。

还可能会想到记录每条零边,然后当两个点的d相等时,这两点间若有零边(不可能各有一条零边)则选择零边的前驱点先出队。很遗憾,这样依旧WA。为什么?且看下图:
在这里插入图片描述
第一种情况能应付,但第二种你应付不了。所以,最后我们选择先拓扑排序一遍(拓扑排序的前提是有向无环图(DAG)),给每个点一个编号,若AB有路径,则A的编号一定比B小,反之不一定成立。
然后,我们改变优先队列的出队逻辑,当两点的d相等时,选择拓扑排序编号小的点先出队。(优先队列的写法是反着来的)

上面两个图的测试用例在代码最后。


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
#include <utility>
using namespace std;

const int INF = 1e9;
const int MAXN = 50;
int N, M, S, E;

vector<pair<int, int>> v[MAXN];
int d[2][MAXN];
bool vis[2][MAXN];
int num[2][MAXN];

//int pre[MAXN][MAXN];
int degree[MAXN];
int no[MAXN];

struct Node
{
	int n, d, id;
	Node(int _n, int _d, int _id) :n(_n), d(_d), id(_id) {}
	bool operator<(const Node& n0) const
	{
		//if (d == n0.d && pre[n][n0.n] != -1)       // 此处 pre[n][n0.n] != -1 一定要加,这样才能过测试1,但过不了测试2
		//	return n != pre[n][n0.n];
		if (d == n0.d)
			return no[n] > no[n0.n];
		return d > n0.d;
	}
};

void init()
{
	for (int i = 0; i < N; i++) v[i].clear();
	//memset(pre, -1, sizeof pre);                         //
	memset(degree, 0, sizeof degree);
	memset(no, 0, sizeof no);
}

void dijkstra(int s)
{
	memset(vis, 0, sizeof vis);
	memset(num, 0, sizeof num);
	fill(d[0], d[0] + N, INF);
	fill(d[1], d[1] + N, INF);

	d[0][s] = 0;
	num[0][s] = 1;

	priority_queue<Node> q;
	q.push(Node(s, d[0][s], 0));

	for (; !q.empty();)
	{
		Node x = q.top();
		q.pop();

		int mind = x.d;
		int u = x.n;
		int id = x.id;

		if (mind == INF) continue;
		if (vis[id][u]) continue;
		vis[id][u] = true;

		for (int i = 0; i < v[u].size(); i++)
		{
			int n = v[u][i].first;
			int w = v[u][i].second;
			if (!vis[0][n] && mind + w < d[0][n])
			{
				d[1][n] = d[0][n];
				num[1][n] = num[0][n];
				d[0][n] = mind + w;
				num[0][n] = num[id][u];

				q.push(Node(n, d[1][n], 1));
				q.push(Node(n, d[0][n], 0));
			}
			else if (!vis[0][n] && mind + w == d[0][n])
			{
				num[0][n] += num[id][u];
			}
			else if (!vis[1][n] && mind + w < d[1][n])
			{
				d[1][n] = mind + w;
				num[1][n] = num[id][u];

				q.push(Node(n, d[1][n], 1));
			}
			else if (!vis[1][n] && mind + w == d[1][n])
			{
				num[1][n] += num[id][u];
			}
		}
	}
}

void topo()
{
	int cnt = 0;
	queue<int> q;
	for (int i = 0; i < N; i++)
		if (degree[i] == 0) q.push(i);
	for (; !q.empty();)
	{
		int x = q.front();
		q.pop();
		no[x] = cnt++;
		for (int i = 0; i < v[x].size(); i++)
		{
			int n = v[x][i].first;
			degree[n]--;
			if (degree[n] == 0)
				q.push(n);
		}
	}
}

int main()
{
	int a, b, c;
	for (; ~scanf("%d%d%d%d", &N, &M, &S, &E);)
	{
		init();
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			v[a].push_back(make_pair(b, c));
			//if (c == 0)  pre[a][b] = pre[b][a] = a;         //
			degree[b]++;
		}
		topo();
		dijkstra(S);
		printf("%d %d\n", d[1][E], num[1][E]);
	}

	return 0;
}

/*测试1
4 5 0 2
0 2 1
0 1 1
1 2 2
1 3 2
3 2 0

3 2*/
  

/*测试2
5 6 0 2
0 2 1
0 1 1
1 2 2
1 3 2
3 4 0
4 2 0

3 2*/

posted @ 2019-04-09 20:09  CrossingOver  阅读(282)  评论(0编辑  收藏  举报