0
点赞
收藏
分享

微信扫一扫

codeforces1631E Paint the Middle(线性DP)

攻城狮Chova 2022-02-12 阅读 57
题目链接:codeforces 1631E
题目思路:

如果数字之间不影响,那么直接考虑贪心,每次选最长的区间,然后中间的改为 1 1 1。但是有可能中间的数字有可能是其他区间的端点。

考虑用动态规划来解决。首先某个数字最后出现的位置一定是区间的右端点,用一个数组 pos 记录某个数字最后出现的位置。定义 dp[i] 为前 i 0 0 0 的个数。为什么是 0 0 0 的个数而不是 1 1 1 的个数呢?因为填 0 0 0 的位置是端点,转移的时候用端点比较好写方程。

显然,第一个数一定是左端点,定义状态转移方程:
d p [ i ] = m i n ( d p [ i − 1 ] + 1 , d p [ i ] ) dp[i] = min(dp[i-1] + 1, dp[i]) dp[i]=min(dp[i1]+1,dp[i])
用一个指针 cur 记录右端点的最大值,让当前最大右端点 ,即 dp[cur],其状态转移方程为:
d p [ c u r ] = m i n ( d p [ i ] + 1 , d p [ c u r ] ) dp[cur] = min(dp[i] + 1, dp[cur]) dp[cur]=min(dp[i]+1,dp[cur])
每次转移的时候,如果当前数字 a i a_i ai 是左端点,那么 dp[cur] 会增加 2 2 2;如果 a i a_i ai 不是左端点,那么 dp[i] 不会更新,dp[cur] 也不会更新。最后答案就是 n-dp[n]

参考代码:
#incldue <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn], pos[maxn], dp[maxn];
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], pos[a[i]] = i;
    memset(dp, 0x3f, sizeof(dp));
    dp[0] = 0;
    int cur = pos[a[1]];
    for (int i = 1; i <= n; i++) {
        dp[i] = min(dp[i-1] + 1, dp[i]);
        cur = max(cur, pos[a[i]]);
        dp[cur] = min(dp[i] + 1, dp[cur]);
    }
    cout << n - dp[n] << endl;
    return 0;
}
举报

相关推荐

0 条评论