Week7 -- 作業 -- C -- TT 的美夢[SPFA-負環-負邊]

題目描述

這一晚,TT 做了個美夢!
在夢中,TT 的願望成真了,他成爲了喵星的統領!喵星上有 N 個商業城市,編號 1 ~ N,其中 1 號城市是 TT 所在的城市,即首都。
喵星上共有 M 條有向道路供商業城市相互往來。但是隨着喵星商業的日漸繁榮,有些道路變得非常擁擠。正在 TT 爲之苦惱之時,他的魔法小貓咪提出了一個解決方案!TT 欣然接受並針對該方案頒佈了一項新的政策。
具體政策如下:對每一個商業城市標記一個正整數,表示其繁榮程度,當每一隻喵沿道路從一個商業城市走到另一個商業城市時,TT 都會收取它們(目的地繁榮程度 - 出發地繁榮程度)^ 3 的稅。
TT 打算測試一下這項政策是否合理,因此他想知道從首都出發,走到其他城市至少要交多少的稅,如果總金額小於 3 或者無法到達請悄咪咪地打出 ‘?’。

輸入

第一行輸入 T,表明共有 T 組數據。(1 <= T <= 50)
對於每一組數據,第一行輸入 N,表示點的個數。(1 <= N <= 200)
第二行輸入 N 個整數,表示 1 ~ N 點的權值 a[i]。(0 <= a[i] <= 20)
第三行輸入 M,表示有向道路的條數。(0 <= M <= 100000)
接下來 M 行,每行有兩個整數 A B,表示存在一條 A 到 B 的有向道路。
接下來給出一個整數 Q,表示詢問個數。(0 <= Q <= 100000)
每一次詢問給出一個 P,表示求 1 號點到 P 號點的最少稅費。

輸出

每個詢問輸出一行,如果不可達或稅費小於 3 則輸出 ‘?’。

樣例輸入

2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
10
1 2 4 4 5 6 7 8 9 10
10
1 2
2 3
3 1
1 4
4 5
5 6
6 7
7 8
8 9
9 10
2
3 10

樣例輸出

Case 1:
3
4
Case 2:
?
?

思路

綜述

這道題主要考察的是SPFA解決單源最短路問題,解決負邊和負環問題;

SPFA

這個算法和dijkstra類似,dijkstra處理的原則是貪心算法,某個點出堆之後不會再入堆,而SPFA算法會在每次鬆弛成功之後都入隊列,所以這個算法是可以實現重複入隊操作;

爲什麼dijkstra算法處理不了帶有負權值的邊的圖

dijkstra是基於貪心策略,每次都找一個距源點最近的點,然後將該距離定爲這個點到源點的最短路徑;但如果存在負權邊,那就有可能先通過並不是距源點最近的一個次優點,再通過這個負權邊,使得路徑之和更小,這樣就出現了錯誤。

爲什麼SPFA可以處理負邊:

因爲在SPFA中每一個點鬆弛過後說明這個點距離更近了,所以有可能通過這個點會再次優化其他點,所以將這個點入隊再判斷一次,

如何判斷成環:

兩個點之間最短路徑:不會超過N-1,所以加一個數組記錄兩點之間的路徑個數即可;

本題邊權如何確定

本題是有向圖,所以邊權就是(目的地繁榮程度 - 出發地繁榮程度)^ 3

過程

Step1:輸入

圖的存儲採用前向星(參考圖論
同樣注意每組數據之間的初始化問題;

Step2:SPFA

負環的發現:兩點之間的邊數大於N-1
此時需要

	for (long long i = 0; i < q; i++) {
		cin >> L >> R;
		cin >> c;
		b[L] += c;
		if (R != n)
			b[R + 1] -= c;
	}

代碼

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <stdio.h>
using namespace std;
const int MAX = 1e6;
int T, N, M, Q;
int tot = 0;
int dis[205];
int vis[205];
int cnt[205];
struct Edge {
	int u, v, w, nxt;
}e[100050];
int head[205];
int a[205];
int fuhuan[205];
void addEdge(int u, int v, int w) {
	e[tot].u = u;
	e[tot].v = v;
	e[tot].w = w;
	e[tot].nxt = head[u];
	head[u] = tot;
	tot++;
}
void init() {
	for (int i = 0; i < 205; i++) {
		head[i] = -1;
		fuhuan[i] = 0;
		dis[i] = MAX;
		vis[i] = 0;
		cnt[i] = 0;
	}
	tot = 0;
}
int S = 1;

void dfs(int u) {
	fuhuan[u] = 1;
	for (int i = head[u]; i != -1; i = e[i].nxt) {
		int v = e[i].v;
		if (!fuhuan[v]) {
			fuhuan[v] = 1;
			dfs(v);
		}
	}
}


void SPFA() {
	queue<int> qq;
	dis[S] = 0;
	vis[S] = 1;
	qq.push(S);
	while (!qq.empty()) {
		int u = qq.front();
		qq.pop();
		if (fuhuan[u])continue;
		vis[u] = 0;
		for (int i = head[u]; i != -1; i = e[i].nxt) {
			int v = e[i].v;
			int w = e[i].w;
			if (dis[v] > dis[u] + w) {
				cnt[v] = cnt[u] + 1;
				//cout << "!!:" << dis[v] << endl;
				//cout << "term:" << cnt[v] << endl;
				if (cnt[v] >= N) {
					//找到負環
					//cout << "QQQ" << endl;
					dfs(v);
				}
				dis[v] = dis[u] + w;
				if (!vis[v]) {
					qq.push(v);
					vis[v] = 1;
				}
			}
		}
	}
}

int main() {
	int x, y;
	cin >> T;
	for (int i = 0; i < T; i++) {
		init();
		cin >> N;
		for (int j = 1; j <= N; j++)cin >> a[j];
		cin >> M;
		for (int j = 0; j < M; j++) {
			cin >> x >> y;
			int num = a[y] - a[x];
			addEdge(x, y, num * num * num);
		}
		cin >> Q;
		SPFA();
		int q;
		cout << "Case " << i + 1 << ":" << endl;
		//cout << "here" << endl;
		//for (int j = 1; j <= N; j++)cout << fuhuan[j] << " ";
		//cout << endl;
		for (int j = 0; j < Q; j++) {
			cin >> q;
			if (fuhuan[q] || dis[q] < 3 || dis[q] == MAX)cout << "?" << endl;
			else cout << dis[q] << endl;
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章