P1983 [NOIP2013 普及组] 车站分级

贪心+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;
}
posted @ 2021-01-26 17:18  yys_c  阅读(436)  评论(0编辑  收藏  举报