Paint(代码源div.1 P4)

本文解析了一道关于区间动态规划的题目,通过理解连续子串对操作次数的影响,利用区间DP求解数组中所有数字相同所需的最少操作次数。博主分享了解题思路和关键代码实现,适合初学者理解区间dp应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随便说点

这周继续补namomonamomonamomo上周的题(苣蒻落泪)。之前在AcwingAcwingAcwing上面学过区间dpdpdp但多少有点板子题的感觉,这题算是给了一个新的切入点。看完题解之后才发现自己怎么这么简单的题目都不会做(苣蒻二落泪)。

题意

给出一个数组,一次操作可以将某一个位置相邻的连续的数字全部转化为另一个相同的数字。请问最少需要操作多少次可以使数组中所有数字相同?

题解

拿到这题我们首先能够明确一个很简单的事实:最多经过n−1n-1n1次操作我们就能达到我们需要的目标,也就是不考虑连续所有数字除了目标数字都被操作一次,那就成了n−1n-1n1

然后我们再来看一下什么情况下能够减少操作的次数——连续。只要我们尽可能多的产生连续的子串我们就能减少操作次数。原生的连续子串我们不用太过于关心,我们应该关心的是我们如何操作才能够减少操作次数。选择一个区间(区间?难道是区间dpdpdp?)a1 a2 … an−1 ana_1\ a_2\ \ldots\ a_{n-1}\ a_na1 a2  an1 an,当我们操作a2 … an−1a_2\ \ldots\ a_{n-1}a2  an1时我们有一点很明确就是经过一系列操作之后这个区间要么等于a1a_1a1要么等于ana_nan,只要等于其中一者我们就能将答案控制在≤n−1\leq n-1n1的范围内,那如果等于两者呢?我们就可以少操作一次。所以我们知道的是我们要尽量选择端点相同的区间,每匹配成功一个区间我们的答案就可以−1-11,所以我们只要找出区间中最多有多少对匹配就能知道答案了。

然后我们只要计算区间中所有的数字出现了几次然后输出就能WAWAWA了。

啊对,你找到数字对数然后就会直接WAWAWA,因为题目中有一种情况是禁止的。假设存在一个数组2 3 2 32\ 3\ 2\ 32 3 2 3,如果按着我们上面说的直接GGG,因为以222为端点的区间和以333为端点的区间相互交错。

我们需要的是dpdpdp。列出区间dpdpdp数组,dp[l][r]dp[l][r]dp[l][r]代表左区间lll和右区间rrr之间有多少配对的数字?

列出状态转移方程
dp[l][r]=max(dp[l−1][r],max(dp[l+1][x−1]+dp[x][r]+1)) w[l]==w[x] dp[l][r]=max(dp[l-1][r],max(dp[l+1][x-1]+dp[x][r]+1))\ w[l]==w[x] dp[l][r]=max(dp[l1][r],max(dp[l+1][x1]+dp[x][r]+1)) w[l]==w[x]
dp[l−1][r]dp[l-1][r]dp[l1][r]代表左端点没有被匹配。

max(dp[l+1][x−1]+dp[x][r]+1)(w[l]==w[x])max(dp[l+1][x-1]+dp[x][r]+1)(w[l]==w[x])max(dp[l+1][x1]+dp[x][r]+1)(w[l]==w[x])是枚举区间中与左端点相同的位置尝试这个区间中有多少配对的点。

当然如果遍历区间中所有的点然后判断其是否与左区间相等O(n3)O(n^3)O(n3)绝对是要时间超限的,所以还要记录一下每个节点的下一个与之相同的节点方便跳指针。

#include<bits/stdc++.h>
using namespace std;
int main(){
#ifdef DEBUG
    freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
#endif
    ios_base::sync_with_stdio(false);
  	cin.tie(0);cout.tie(0);
    int _;
    cin >> _;
    while(_ --){
        int n;
        cin >> n;
        vector<int> w(n + 10);
        for (int i = 1;i <= n;i ++) cin >> w[i];
        vector<int> nxt(n + 10);
        vector<int> pos(n + 10);
        for (int i = 1;i <= n;i ++) pos[i] = n + 1;
        for (int i = n;i >= 1;i --) {
            nxt[i] = pos[w[i]];
            pos[w[i]] = i;
        }
        vector<vector<int>> dp(n + 10,vector<int> (n + 10));
        for (int len = 1;len <= n;len ++) {
            for (int l = 1;l <= n;l ++) {
                int r = l + len - 1;
                if(r > n) break;
                dp[l][r] = dp[l + 1][r];
                int x = nxt[l];
                while(x <= r) {
                    dp[l][r] = max(dp[l][r], dp[l + 1][x - 1] + dp[x][r] + 1);
                    x = nxt[x];
                }
            }
        }
        cout << n - 1 - dp[1][n] << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值