拓撲排序-HDU2647 Reward

拓撲排序

什麼是拓撲排序?
簡單來說,在做一件事之前必須先做另一(幾)件事都可抽象爲圖論中的拓撲排序,比如課程學習的先後,安排客人座位等。
一個圖能拓撲排序的充要條件是它是有向無環圖。將問題轉爲圖,比如A指向B代表完成B前要先完成A,那麼用數組記錄入度,從入度爲0的開始搜索(bfs/dfs)和維護數組,即可得到拓撲排序。

例題

傳送門: HDU-2647

Dandelion’s uncle is a boss of a factory. As the spring festival is coming , he wants to distribute rewards to his workers. Now he has a trouble about how to distribute the rewards.
The workers will compare their rewards ,and some one may have demands of the distributing of rewards ,just like a’s reward should more than b’s.Dandelion’s unclue wants to fulfill all the demands, of course ,he wants to use the least money.Every work’s reward will be at least 888 , because it’s a lucky number.

Input:

One line with two integers n and m ,stands for the number of works and the number of demands .(n<=10000,m<=20000)
then m lines ,each line contains two integers a and b ,stands for a’s reward should be more than b’s.

Output:

For every case ,print the least money dandelion ‘s uncle needs to distribute .If it’s impossible to fulfill all the works’ demands ,print -1.

Sample Input:

2 1
1 2
2 2
1 2
2 1

Sample Output:

1777
-1

題意

每組輸入兩個數n(人數),m(關係數)。接下來的m行輸入a,b表示a的獎金大於b,其中每個人獎金至少爲888,求老闆至少發多少錢,若不能合理分配則輸出-1。

分析

用need[]存入度數,再用數組add[]表示某人在888的基礎上多發的錢。先將入度爲0的入隊,然後bfs並維護數組,因爲求至少發的錢,那麼a比b多1即可。最後用cnt判斷求出來點數是否等於總數,排除有環情況。

代碼

#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 20004;
int n, m, x, y;
int main() {
	while (~scanf("%d%d", &n, &m)) {
		vector<int>v[maxn];
		int need[maxn] = { 0 };//入度
		int add[maxn] = { 0 };//某人至少多發多少錢才滿足要求
		while (m--) {
			scanf("%d%d", &x, &y);
			v[y].push_back(x);//x比y多(y指向x):即求x前要先求y
			need[x]++;
		}
		queue<int>q;
		for (int i = 1; i <= n; i++)
			if (need[i] == 0)
				q.push(i);//入度爲0的先入隊
		int cnt = 0;
		while (q.empty() == false) {
			int cur = q.front();
			q.pop();
			cnt++;
			for (int i = 0; i < v[cur].size(); i++)
				if (--need[v[cur][i]] == 0) {
					q.push(v[cur][i]);
					add[v[cur][i]] = max(add[v[cur][i]], add[cur] + 1);
				}
		}
		if (cnt == n) { //檢查有環情況
			ll ans = 888 * n;
			for (int i = 1; i <= n; i++)
				ans += add[i];
			printf("%lld\n", ans);
		}
		else puts("-1");
	}
	return 0;
}

小結

  1. 符合條件的拓撲排序可能有多種,按題目要求(如字典序等)使用優先隊列來實現即可。
  2. 用反向建圖解決某些奇奇怪怪?的排序要求
  3. 部分題的坑點:重複輸入關係,需要特判一下。

你的點贊將會是我最大的動力

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章