1. 程式人生 > 其它 >AcWing 456. 車站分級

AcWing 456. 車站分級

原題連結AcWing 456. 車站分級

抽象出題意,停靠過的車站的等級一定嚴格大於為停靠過的車站的等級,且不存在環,例如車站\(A\)等級大於車站\(B\),則\(A >= B + 1\),不妨從\(B\)\(A\)連一條邊,表示等級關係,題目要求車站的最小等級中最大是多少,即求最長路,那這就是一個差分約束系統。

而對於差分約束系統:
如果邊權有正有負:則使用\(spfa\)
如果邊權非負,那麼可以使用\(tarjan\)縮點+遞推,\(拓撲排序 + 遞推\)的方式求最長路或者最短路。

同時,對於本題,把未停靠的站點和停靠的分成兩個集合,那麼需要連邊最壞情況下是要\(n^2\),考慮最壞情況下,一共有\(1000\)

趟車,每一趟都是從1~n站,其中停靠了\(500\)個站,還有\(500\)個未停靠,那麼這時候連邊就是\(500 * 500 * 1000 = 250000000\),顯然,複雜度爆炸,但是考慮一種優化,在集合中間建立虛擬結點,左邊邊權是\(0\),右邊邊權是\(1\),那麼就優化成了\(O(n + m)\)連邊方式了,再算一下最壞情況下的資料量,\((500 + 500) * 1000 = 1000000\),這樣就可以過了,直接優化成了線性,並且完全等價於\(O(n^2)\)連邊的方式。

// Problem: 車站分級
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/458/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

const int N = 2010, M = 1E6 + 10;

int h[N], e[M], ne[M], w[M], idx;
int n, m;
int d[N];
int dist[N];
int seq[N], cnt;
bool st[N];

void add(int a, int b, int c) {
	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
	d[b]++;
}

void topsort() {
	queue<int> q;
	for (int i = 1; i <= n + m; i++) {
		if (!d[i]) q.push(i);
	}
	
	while (q.size()) {
		int t = q.front();
		q.pop();	
		
		seq[cnt++] = t;
		
		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i];
			if (--d[j] == 0) q.push(j);
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof h);
	for (int i = 1; i <= m; i++) {
		int cnt;
		scanf("%d",  &cnt);
		memset(st, 0, sizeof st);
		int start = n, end = 1;
		while (cnt--) {
			int k;
			scanf("%d", &k);
			start = min(start, k);
			end = max(end, k);
			st[k] = true;
		}	
		
		int vir = n + i;
		for (int j = start; j <= end; j++) {
			if (!st[j]) add(j, vir, 0);
			else add(vir, j, 1);
		}
	}
	
	topsort();
		
	for (int j = 1; j <= n; j++) dist[j] = 1;
	for (int j = 0; j < n + m; j++) {
		int var = seq[j];
		for (int k = h[var]; ~k; k = ne[k]) {
			dist[e[k]] = max(dist[e[k]], dist[var] + w[k]);
		}
	}
	
	int res = 0;
	for (int i = 1; i <= n; i++) res = max(res, dist[i]);
	printf("%d\n", res);
	
    return 0;
}