HDU 3986 Harry Potter and the Final Battle

传送门

这道题和1595基本一样,但是有两个根本不同:

  1. 这道题明确说了有重边
  2. 这道题说不一定连通

对于第一点,用邻接表就好了,在最短路算法中记录前驱,注意,记录的是前驱的边的索引,然后回溯的时候每次删一条边,体会这个循环的写法 for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)。另外,删边其实还是把边权置为INF,然后最短路算法里要加上判断当前边权不等于INF的条件(本来用邻接表是不用加的)。

第二点,不连通输出-1,说明不保证连通,这个不连通分为两种不连通,一个是什么都不删(原图)也不连通,另一个是删边会不连通。对于第一种,本来就不连通,必须直接输出答案了,因为这时pre[N]一定等于初始值-1,根本就没有最短路,若是让他进去了上面那个回溯循环中那还了得?边表下标都成了-1,简直瞎搞。(找这个bug大概花了半小时)
对于第二种不连通,原本代码是没问题的,但是可以再加快一点速度,若这次删边导致不连通了就可以直接退出循环了。

顺便一说,这道题可以体会SPFA的威力。
下面这个 234ms,SPFA

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

const int INF = 1e9;
const int MAXN = 1001;
int N, M, T;

struct Edge
{
	int n, w;
};
vector<Edge> ve;
vector<int> v[MAXN];
int pre[MAXN];
int d[MAXN];
bool inq[MAXN];
int ans;

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

void spfa(int s, int flag)
{
	queue<int> q;
	memset(inq, 0, sizeof inq);
	fill(d + 1, d + N + 1, INF);
	q.push(s);
	inq[s] = true;
	d[s] = 0;

	for (; !q.empty();)
	{
		int u = q.front();
		q.pop();
		inq[u] = false;
		int n, w;
		for (int i = 0; i < v[u].size(); i++)
		{
			n = ve[v[u][i]].n;
			w = ve[v[u][i]].w;
			if (w == INF) continue;          // 本来邻接表是不用这句的
			if (d[u] + w < d[n])
			{
				d[n] = d[u] + w;
				if (flag) pre[n] = v[u][i];
				if (!inq[n])
				{
					q.push(n);
					inq[n] = true;
				}
			}
		}
	}
}

int main()
{
	int a, b, c;
	scanf("%d", &T);
	for (; T--;)
	{
		scanf("%d%d", &N, &M);
		init();
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			ve.push_back(Edge{ b,c });
			ve.push_back(Edge{ a,c });
			v[a].push_back(i << 1);
			v[b].push_back(i << 1 | 1);
		}
		spfa(1, 1);
		if (d[N] == INF)                        // 非常重要
		{
			printf("-1\n");
			continue;
		}
		//ans = max(ans, d[N]);                 这句话没有一点用处

		int t, e1, e2;
		for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)            ////
		{
			e1 = pre[x];
			e2 = e1 ^ 1;

			t = ve[e1].w;
			ve[e1].w = ve[e2].w = INF;             // 没法删去,只能临时把边权改成无穷大,相当于这条边没有了
			spfa(1, 0);
			ans = max(ans, d[N]);
			if (ans == INF) break;                 // 加快一点速度
			ve[e1].w = ve[e2].w = t;
		}
		if (ans == INF)
			printf("-1\n");
		else printf("%d\n", ans);
	}

	return 0;
}

下面这个 1388ms,dijkstra


#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, T;

struct Edge
{
	int n, w;
};
vector<Edge> ve;
vector<int> v[MAXN];
int pre[MAXN];
int d[MAXN];
bool vis[MAXN];
int ans;

void init()
{
	ve.clear();
	for (int i = 1; i <= N; i++) v[i].clear();
	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;
		int n, w;
		for (int j = 0; j < v[u].size(); j++)
		{
			n = ve[v[u][j]].n;
			w = ve[v[u][j]].w;
			if (w == INF) continue;                    // 本来邻接表是不用这句的
			if (!vis[n] && d[u] + w < d[n])
			{
				d[n] = d[u] + w;
				if (flag)
					pre[n] = v[u][j];
			}
		}
	}
}

int main()
{
	int a, b, c;
	scanf("%d", &T);
	for (; T--;)
	{
		scanf("%d%d", &N, &M);
		init();
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			ve.push_back(Edge{ b,c });
			ve.push_back(Edge{ a,c });
			v[a].push_back(i << 1);
			v[b].push_back(i << 1 | 1);
		}
		dijkstra(1, 1);
		if (d[N] == INF)
		{
			printf("-1\n");
			continue;
		}
		//ans = max(ans, d[N]);

		int t, e1, e2;
		for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)            ////
		{
			e1 = pre[x];
			e2 = e1 ^ 1;

			t = ve[e1].w;
			ve[e1].w = ve[e2].w = INF;             // 没法删去,只能临时把边权改成无穷大,相当于这条边没有了
			dijkstra(1, 0);
			ans = max(ans, d[N]);
			if (ans == INF) break;                 // 不加这行2542ms,加上这行1388ms
			ve[e1].w = ve[e2].w = t;
		}
		if (ans == INF)
			printf("-1\n");
		else printf("%d\n", ans);
	}

	return 0;
}
posted @ 2019-03-30 19:14  CrossingOver  阅读(139)  评论(0编辑  收藏  举报