zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    高级排序
    递归
    Linked List
    中缀、后缀、前缀表达式
    队列(queue)
    栈(Stack)
    数组(Array)
    数据结构和算法
    常见排序
    开启
  • 原文地址:https://www.cnblogs.com/tomori/p/14331510.html
Copyright © 2011-2022 走看看