HDU 1595 find the longest of the shortest

版权声明:本人QQ 249712949 欢迎交流算法(请备注)。 https://blog.csdn.net/coord_/article/details/88913019

传送门

最短路。
给一个无向图,随机删去一条边,问你在所有可能的删边情况下的给定两点间的最短路的最大值。

想象删的这条边如果不在原最短路上,那么无影响,只有在原最短路上删边才可能会造成最短路的值提升。那么就在原最短路上一条边一条边的枚举好了。

这道题说边数M<=N*(N-1)/2,所以可以认为应该没有重边。对这道题而言,有没有重边非常重要,如果有重边,就不能再用邻接矩阵了(因为邻接矩阵默认每两点间只有一条边,即使加上g[a][b] = min(g[a][b], c)也没用,它只是多条重边求最小值,它完全不能表达出两点间有多条边的意义),想象最短路上的每条边都有一个复制品,那么答案应该还是原最短路的值,而如果让邻接矩阵处理的话直接把两点间的两条边都删没了(g[x][pre[x]] = INF)。
所以,如果有重边的话,要用邻接表,最短路算法中的前驱记录该边在边表中的下标,删边的时候根据边表下标来删。

还有,这是个无向图,所有1->NN->1是一样的。


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
using namespace std;                              // 枚举初始最短路的边,删之,重新求最短路,去其最大值。

const int INF = 1e9;
const int MAXN = 1001;
int N, M;
int g[MAXN][MAXN];
int pre[MAXN];
int d[MAXN];
bool vis[MAXN];
int ans;

void init()
{
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= N; j++)
		{
			if (i == j) g[i][j] = 0;
			else g[i][j] = INF;
		}
	ans = -1;
	memset(pre, -1, sizeof pre);
}

void dijkstra(int s, int flag)
{
	memset(vis, 0, sizeof vis);
	fill(d + 1, d + N + 1, INF);
	d[s] = 0;
	for (int i = 0; i < N; i++)
	{
		int u = -1, mind = INF;
		for (int j = 1; j <= N; j++)
		{
			if (!vis[j] && d[j] < mind)
			{
				mind = d[j];
				u = j;
			}
		}
		if (u == -1) break;
		vis[u] = true;
		for (int j = 1; j <= N; j++)
		{
			if (!vis[j] && g[u][j] != INF)
			{
				if (d[u] + g[u][j] < d[j])
				{
					d[j] = d[u] + g[u][j];
					if (flag)
						pre[j] = u;
				}
			}
		}
	}
}

int main()
{
	int a, b, c;
	for (; ~scanf("%d%d", &N, &M);)
	{
		init();
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			g[a][b] = g[b][a] = c;       // 无向图,这道题说了 M<=N*(N-1)/2,所以应该是不会有重边的,若有重边,绝对不能用邻接矩阵了
		}
		dijkstra(1, 1);
		//ans = max(ans, d[N]);           这句根本没用
		int t;
		for (int x = N; pre[x] != -1; x = pre[x])
		{
			t = g[x][pre[x]];
			g[x][pre[x]] = g[pre[x]][x] = INF;            // 删边
			dijkstra(1, 0);
			ans = max(ans, d[N]);
			g[x][pre[x]] = g[pre[x]][x] = t;              // 恢复该边,去删下一条边
		}
		printf("%d\n", ans);
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/coord_/article/details/88913019