HDU2433 travel (最短路徑樹)

題面如圖

在這裏插入圖片描述

分析

直接做的複雜度是 O(nmm)O(n*m*m),問題在於每割一條邊對每個點都要做一次最短路,而其實某些點的最短路並沒有發生變化。
於是我們有一個優化的思路:
如果這條邊不在某些點爲出發點的最短路上,顯然不用從這個點跑一次最短路;如果在的話就跑一次最短路

如何實現這個操作呢?
我們先可以先處理出每一個點出發的最短路路徑(由於邊長是 11,於是用 bfsbfs 即可),這其實是一顆以 aa 爲頂點的生成樹(如果最短路有多種方案,只記一種)。用 u[a][b][c]u[a][b][c] 表示 bcb\rightarrow c 這條邊是否在 aa 爲頂點的生成樹上。每次枚舉邊的時候判斷一下,如果在以 ii 爲根的生成樹上,就跑一次最短路。

思考一下複雜度?
nn 棵樹, 一棵樹只有 n1n-1 條邊,也就是說在樹上的邊數爲
min(n(n1),3000)<3000min(n*(n-1),3000) < 3000,所以最多跑 30003000 次最短路,每次最短路是 O(m)O(m) 的,所以總複雜度是 O(mm)O(m*m)

代碼如下

#include <bits/stdc++.h>
#define N 105
#define M 3005
#define inf 2147483647
using namespace std;
struct node{
	int a, b, c, n;
	bool operator < (const node & A) const{
		return c > A.c;	
	}
}d[M * 2];
int h[N], cnt, dis[N], s[N], t[N], n, m, v[M * 2], ret[N][N], u[N][N][N], flag;
void cr(int a, int b, int c){
	d[++cnt].a = a; d[cnt].b = b; d[cnt].c = c; d[cnt].n = h[a]; h[a] = cnt;
}
void bfs(int x, int p){
	int i, a, b;
	if(!p) memset(u[x], 0, sizeof(u[x]));
	s[x] = 0;
	queue<int> q;
	q.push(x);
	memset(dis, -1, sizeof(dis));
	dis[x] = 0;
	while(!q.empty()){
		a = q.front(); q.pop();
		for(i = h[a]; i; i = d[i].n){
			if(v[i]) continue;
			b = d[i].b;
			if(dis[b] > -1) continue;
			dis[b] = dis[a] + 1;
			if(!p) u[x][a][b] = u[x][b][a] = 1;
			q.push(b);
		}
	}
	for(i = 1; i <= n; i++){
		if(dis[i] == -1) flag = 1;
		s[x] += dis[i];
	}
}
int main(){
	int i, j, k, a, b, sum;
	while(scanf("%d%d", &n, &m) != EOF){
		memset(h, 0, sizeof(h));
		memset(ret, 0, sizeof(ret));
		cnt = flag = sum = 0;
		for(i = 1; i <= m; i++){
			scanf("%d%d", &a, &b);
			cr(a, b, 1);
			cr(b, a, 1);
			ret[a][b]++;
			ret[b][a]++;
		}
		for(i = 1; i <= n; i++) bfs(i, 0), t[i] = s[i];
		if(flag){
			for(i = 1; i <= m; i++) printf("INF\n");
			continue;
		}
		for(i = 1; i <= cnt; i += 2){
			flag = sum = 0;
			a = d[i].a; b = d[i].b;
			v[i] = v[i + 1] = 1;
			for(j = 1; j <= n; j++){
				if(!u[j][a][b] || ret[a][b] > 1) sum += t[j];
				else{
					//printf("%d %d %d===\n", j, a, b);
					bfs(j, 1);
					sum += s[j];
					if(flag) break;
				}
			}
			if(flag) printf("INF\n");
			else printf("%d\n", sum);
			v[i] = v[i + 1] = 0;
		}
	}
	return 0;
}
發佈了85 篇原創文章 · 獲贊 80 · 訪問量 4014
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章