• CF713C Sonya and Problem Wihtout a Legend (经典dp)


    一个经典的通过增长减小大小来求使得序列单调性的最小代价。

    对于这道题,有一个前置题是不要求要严格单调,而是不严格单调

    这样的话,我们可以得到一个性质,最后所有的值都是在原序列当中的,这其实用贪心的想法想一想就好,因为一旦通过加减与左边或右边相等,就没必要再加减了

    但是本题要求严格,这就很难说了,因此要考虑将一个问题转化成已知的问题

    对于原先的问题,其实就是a[i]-a[j]>=0就好

    那么现在的问题是a[i]-a[j]>=i-j,因此我们只要对输入的原数列减下标i,就转化成上面的问题了

    也就是 a[i]-i>=a[j]-j。

    对于dp问题,要进行集合划分,而对于这样的数值问题,以及这样的数据范围为,自然考虑到二维状态,第一维自然是前i个,重点是第二维,我们一般要寻找题目的信息

    而第二维的思考就是考虑如何能将所有状态划分,一般来说都可以考虑最后一位不同的不同情况。因此,这次我们可以设计为前i个,第i个的值是j的最小值,因为根据上文所说,j永远都是原数列中的值

    但是这样的话需要多枚举一层,因为我们要到前面找不同的k,1<=k<=j,才能完整的求,所以又出现了一个常见的优化,我们将dp状态改为前i个,第i个最大是j的情况。这样就可以通过前缀优化

    本题要离散化,且爆int

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<map>
    #include<algorithm>
    #include<queue>
    #include<set>
    #define ull unsigned long long
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=1e5+10;
    ll f[3010][3010];
    vector<ll> num;
    ll a[N];
    int main(){
        int n;
        cin>>n;
        int i;
        for(i=1;i<=n;i++){
            cin>>a[i];
            a[i]-=i;
            num.push_back(a[i]);
        }
        memset(f,0x3f,sizeof f);
        sort(num.begin(),num.end());
        num.erase(unique(num.begin(),num.end()),num.end());
        int cnt=num.size();
        for(i=1;i<=cnt;i++){
            f[1][i]=abs(a[1]-num[i-1]);
            if(i>1){
                f[1][i]=min(f[1][i],f[1][i-1]);
            }
        }
        int j;
        for(i=2;i<=n;i++){
            for(j=1;j<=cnt;j++){
                f[i][j]=min(f[i][j-1],f[i-1][j]+abs(a[i]-num[j-1]));
            }
        }
        cout<<f[n][cnt]<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    浅谈动态开点线段树
    详解二叉查找树(BST)
    数组的随机打乱
    浅谈迭代加深搜索
    浅谈传递闭包问题
    详解权值线段树
    算法竞赛中桶的概念与应用
    NKOJ 1353 图形面积
    并查集 & 最小生成树详细讲解
    【线段树基础】NKOJ 1321 数列操作
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/12638176.html
Copyright © 2020-2023  润新知