P1983 [NOIP2013 普及组] 车站分级

P1983 NOIP2013 普及组 车站分级

差分约束
左边的点代表不必停靠的车站, 右边的点代表需要停靠的车站
对于任意左边的点 x 和右边的点 y , 则 x < y 即 x + 1 <= y

o		o				o \   / o
o		o				o - o - o
o		o				o /	  \ o
		o						o
   图1						图2

若左边的每个点和右边的每个点连一条边权为 1 的边, 则边数太多, 会爆空间, 进而考虑优化:
在这两列点中间加上一个虚拟点, 左边的点与虚拟点链一条边权为 0 的边, 右边的点与虚拟点连一条边权为 1 的边即可

点击查看代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

const int N = 2005, M = 1000005;



int n, m;
int h[N], e[M], w[M] ,nxt[M], idx; // 需要建图进行拓扑排序
int d[N];					 // 距离
bool st[N]; // 是否需要停靠站(辅助数组)

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

//	从这个点往后走最多能到几个点
int dfs(int u) {
	if(d[u] != -1) return d[u]; // 记忆化搜索
	d[u] = 1;
	for(int i = h[u]; i; i = nxt[i]) {
		int j = e[i];
		d[u] = max(d[u], dfs(j) + w[i]);
	}
	return d[u];
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i ++) {
		int cnt, start = n, end = 1; // start 和 end 分别表示是始发站的编号和终点站的编号
		scanf("%d", &cnt);
		memset(st, false, sizeof(st));
		while(cnt --) {
			int x;
			scanf("%d", &x);
			st[x] = true;
			start = min(start, x);
			end = max(end, x);
		}
		for(int j = start; j <= end; j ++)
			if(st[j]) add(n + i, j, 1); // 与虚拟点连边
			else	  add(j, n + i, 0);
	}
	int res = 0;
	memset(d, -1, sizeof(d));
	for(int i = 1; i <= n; i ++) res = max(res, dfs(i));
	printf("%d\n", res);
	return 0;
}
posted @ 2022-09-27 15:36  azzc  阅读(93)  评论(0编辑  收藏  举报