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;
}