2020-2021 ACM-ICPC Latin American Regional Programming Contest(F. Fascinating Partitions)

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020-2021 ACM-ICPC Latin American Regional Programming Contest(F. Fascinating Partitions)相关的知识,希望对你有一定的参考价值。

题意:给n个数的数组,将数组划分成k个子数组,划分的费用是子数组中的最大值,对k从1到n求划分的最小和最大费用。(n<=8000)
Solution:
\\quad 最大费用很显然每次取数组中最大值即可。
\\quad 最小费用需要dp一下,容易想到n^3的dp,dp[i][j]表示前i次划分,到第j个数的最小费用。
\\quad 有dp[i][j] = min( dp[i-1][k] + cost(k…j) )
对于每个 a i a_i ai要么作为区间最大值,要么无作用。再挖掘一下性质,因为花费是最大值,设 a m a_m am a i a_i ai左边一个比 a i a_i ai大的数,也就是说 a j < = a i , j ∈ ( m , i ) a_j<=a_i,j∈(m,i) aj<=ai,j(m,i)
在第k次划分中,若ai作为最大值,则有dp[k][j] = min(dp[k][j] , dp[k-1][j] + a[i])
若不作为最大值,那么只要直接继承位置m的dp值,因为 a m a_m am把区间[m+1,i]都给覆盖了。
\\quad 用单调栈维护出这样的m,和对应的值,所谓的值 = dp值 - a m a_m am,这样是为了快速算上面第一种情况,就是算上面的dp[k-1][j]。对于位置m,我们只要知道m具体是什么,就可以得到位置m的dp值了。有点难讲,上代码理解应该好一点。
PS:没想到划分的轮次可以独立考虑,之前想的方程是dp[i][j]前i个,划分成j组。

#define int long long
const int N=8e3+10;
int n,a[N];
int ans[2][N];
int f[N],g[N];
signed main()
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    ios;
    cin>>n;
    std::vector<int> v;
    _for(i,1,n) cin>>a[i],v.push_back(a[i]);
    sort(all(v),greater<int>());
    //算花费最大值
    int S = 0 , t = 0;
    for(int i:v) 
        S += i;
        ans[1][++t] = S;
    
    //算花费最小值
    _for(i,0,n) f[i] =  INF;
    _for(i,1,n)
        vector<pii> sta;
        swap(f,g);
        _for(j,1,n) f[j] = INF;
        _for(j,i,n)
            //g[j-1]表示划分前j-1个成i-1组的最小费用
            int t = INF , mv = INF;
            t = g[j-1];
            //单调栈维护递减aj值和对应的值 , 值 + a[j] = dp,就是dp - a[j]
            while( !sta.empty() && a[sta.back().fi] < a[j] ) 
                t = min(t,sta.back().se);    
                sta.pop_back();            
            
            if( sta.empty() )
                if( i==1 ) t = 0;
            
            else //如果aj不选,只要看前面第一个大于aj的dp值
                mv = f[sta.back().fi];
            
            sta.push_back(j,t);
            f[j] = min(t+a[j] , mv);
        
        ans[0][i] = f[n];
    
    _for(i,1,n) cout<<ans[0][i]<<" "<<ans[1][i]<<endl;
    AC; 


以上是关于2020-2021 ACM-ICPC Latin American Regional Programming Contest(F. Fascinating Partitions)的主要内容,如果未能解决你的问题,请参考以下文章

训练20191007 2017-2018 ACM-ICPC Latin American Regional Programming Contest

2017-2018 ACM-ICPC Latin American Regional Programming Contest

2019-2020 ACM-ICPC Latin American Regional Programming Contest L - Leverage MDT

2017-2018 ACM-ICPC Latin American Regional Programming Contest GYM101889

2017-2018 ACM-ICPC Latin American Regional Programming Contest Solution

2019-2020 ACM-ICPC Latin American Regional Programming Contest A- Algorithm Teaching 二分图