C. Sequence

VMware OA 中很难的一题。先从简单的问题开始考虑。

C. Sequence  http://codeforces.com/problemset/problem/13/C

lemma:如果操作的次数最少,那么最终得到的不降的数列,必然是由原始数列中的数组成的,具体的证明可以使用反证法。由此我们可以通过限定整个数组递增的上限来dp。

本题的核心是二维dp,第一维i很常规,难点是第二维。由于数组元素较大,我们离散化表示这个cap。我们copy一份数组,进行排序称为b,用b[j]来限定数组递增的上限。

dp[i][j] 使得前i个元素递增,且最大元素(a[i-1]) <= b[j] 的最小cost

dp[i][j] = min { dp[i-1][j]+|a[i-1]-b[j]|, dp[i][j-1] }

base: dp[0][j]=0

result: dp[n][n-1]

用前i个元素的好处就是初始化dp[0][j]为0后,dp[1][j]就可以合并入for循环中。

递推公式比较tricky。如图所示,在更新 dp[i][j] 的时候,有以下可能:

1. a[i-1] >= b[j],这种情况只能把 a[i-1] 减小到 b[j]。

2. a[i-1] < b[j],因为b有序,b[j-1]<b[j],而a[i-1]和b[j-1]都是数组里的元素,推出 a[i-1] <= b[j-1]。符合 dp[i][j-1] 的定义!

观察递推公式,发现可以用滚动数组优化,使得空间复杂度 O(n)

int csequence(vector<int> &a){
    vector<int> b=a;
    sort(b.begin(),b.end());
    // dp[i][j] 使得前i个元素递增,且最大元素(a[i-1]) <= b[j] 的最小cost
    // dp[i][j] = min { dp[i-1][j]+|a[i-1]-b[j]|, dp[i][j-1] }
    // base: dp[0][j]=0
    // result: dp[n][n-1]
    int n=a.size();
    vector<int> dp(n,0);
    for (int i=1;i<=n;++i){
        for (int j=0;j<n;++j){
            dp[j] = dp[j]+abs(a[i-1]-b[j]);
            if (j>0) dp[j] = min(dp[j],dp[j-1]);
        }
    }
    return dp[n-1];
}


int main() {
    //vector<int> a({3,2,-1,2,11});
    vector<int> a{2,1,1,1,1};
    cout << csequence(a);
    return 0;
}

时间复杂度 O(n^2)

由于本题既可以是递增也可以是递减,所以reverse以后再求递增取小即可。

int csequence(vector<int> &a){
    vector<int> b=a;
    sort(b.begin(),b.end());
    // dp[i][j] 使得前i个元素递增,且最大元素(a[i-1]) <= b[j] 的最小cost
    // dp[i][j] = min { dp[i-1][j]+|a[i-1]-b[j]|, dp[i][j-1] }
    // base: dp[0][j]=0
    // result: dp[n][n-1]
    int n=a.size();
    vector<int> dp(n,0);
    for (int i=1;i<=n;++i){
        for (int j=0;j<n;++j){
            dp[j] = dp[j]+abs(a[i-1]-b[j]);
            if (j>0) dp[j] = min(dp[j],dp[j-1]);
        }
    }
    return dp[n-1];
}

int climbhill(vector<int> &a){
    int increase=csequence(a);
    reverse(a.begin(),a.end());
    int decrease=csequence(a);
    cout << increase << ' ' << decrease << endl;
    return min(increase,decrease);
}


int main() {
    vector<int> a{9,8,7,2,3,3};
    cout << climbhill(a);
    return 0;
}

时间复杂度 O(n^2)

猜你喜欢

转载自www.cnblogs.com/hankunyan/p/11346263.html