贪心+dp
题意:有n个车站,m个车,每一个车有一个行程路线(哪个站停,那个站不停),但是所有车的停靠必须遵守规则:每一个车站都有一个等级,在一辆车从起始站 -> 终点站(停靠站包括这两个)的过程中,如果最早在x站(它的等级为P)停靠了,那么后面所有的等级 >= P的车站k都要停(注意这里,如果车从k再向后开,那么会有叠加),而等级 < P的可停可不停。
注意点:只有在x站停靠了,才会造成影响,如果没在x站停,那么x站的等级P不对后面的停车起作用。
分析:
另外,如果车次只有一个,那么不管这个车是怎么走的,最少对站台划分的等级数量均为2,且至少是2,因为这个车的行程必定会分成“停”和“不停”两部分,并且有不停的站台的等级 < 停的站台的等级,所以只需要让所有停的站台的等级 = a,所有不停的站台的等级 = b,并让b < a即可,所以最少两级。
贪心建图:让每一个车次所造成的等级数都最小(即停的站和不停的站内部都不在分级,只考虑他们之间的关系),以车站作为结点,等级的小于关系作为连边的依据,对于每一个车次,让它的不停的站去指向所有停的站,最后会得到一个DAG(因为题目给的数据都是正确的,不会出现a < b < c < a的情况)
由于划分的等级数要让所有的车的行程都是正确的,所以求DAG的最长路即可。
状态:(f(i))表示从(i)开始的路径集合,存储最大长度
状态计算:(f(i) = 1 + max{f(j)},i)在DAG上有到(j)的边
初始状态:(f(k) = 1,k)没有后继结点
存图的方法:
极限数据下:n = m = 1000
对于每一个车次,x为不停的站则需要加边的条数为:x(1000 - x), 这个函数的最大值在x = 500处取为250000,那么总共要加边的条数为1000 * 250000 = (2.5 * 10^8),是稠密图,用邻接矩阵存。
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N = 1010;
int n, m;
int g[N][N];
int st[N];
int f[N];
int dfs(int u){
if(f[u]) return f[u];
f[u] = 1;
for(int i = 1; i <= n; i ++)
if(g[u][i]) f[u] = max(f[u], 1 + dfs(i));
return f[u];
}
int main(){
cin >> n >> m;
while(m --){
memset(st, 0, sizeof st);
int s;
cin >> s;
vector<int> stops;
while(s --){
int t;
cin >> t;
st[t] = 1;
stops.push_back(t);
}
// 加边
for(int i = stops[0]; i <= stops.back(); i ++)
if(st[i] == 0)
for(auto stop : stops)
g[i][stop] = 1;
}
int res = 0;
for(int i = 1; i <= n; i ++) res = max(res, dfs(i));
cout << res << endl;
return 0;
}
加虚点优化:把加边的复杂度从(O(n^2))改为(O(n)),但是虚点要特判
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N = 2010;
int n, m;
int g[N][N];
int st[N];
int f[N];
int dfs(int u){
if(f[u]) return f[u];
f[u] = 1;
for(int i = 1; i <= n + m; i ++)
if(g[u][i])
if(i > n) f[u] = max(f[u], dfs(i));
else f[u] = max(f[u], 1 + dfs(i));
return f[u];
}
int main(){
cin >> n >> m;
for(int i = 1; i <= m; i ++){
memset(st, 0, sizeof st);
int s;
cin >> s;
vector<int> stops;
while(s --){
int t;
cin >> t;
st[t] = 1;
stops.push_back(t);
}
// 虚点 n + i
for(auto stop : stops) g[n + i][stop] = 1;
for(int j = stops[0]; j <= stops.back(); j ++)
if(st[j] == 0) g[j][n + i] = 1;
}
int res = 0;
for(int i = 1; i <= n; i ++) res = max(res, dfs(i));
cout << res << endl;
return 0;
}