Zuma HDU - 6212(区间dp)

本文解析了一道关于Zuma游戏的动态规划算法题目,通过将游戏状态抽象为区间消去问题,详细阐述了如何利用DP求解最小插入球数以清除所有球。文章深入分析了三种消去策略,提供了完整的代码实现。

Zuma HDU - 6212

Think about the Zuma Game. You have a row of at most 200
black(0) or white(1) balls on the table at the start. Each three consecutive balls never share the same colour. You also have infinite amount of black and white balls in your hand. On each turn, you can choose a ball in your hand and insert it into the row, including the leftmost place and the rightmost place. Then, if there is a group of three of more balls in the same colour touching, remove these balls. Keep doing this until no more balls can be removed.
Find the minimal balls you have to insert to remove all the balls on the table.
Input
The first line of input contains an integer T (1≤T≤100) which is the total number of test cases.
Each test case contains a line with a non-empty string of 0 and 1
describing the row of balls at the start.
Output
For each test case, output the case number and the minimal balls required to insert in a line.
Sample Input

4
10101
101001001
1001001001
01001101011001100

Sample Output

Case #1: 4
Case #2: 3
Case #3: 3
Case #4: 2
题意:

一共有两种颜色的祖玛游戏,每三个连在一起(或者大于三个)的球球就会被消除掉,问将这个字符串消除干净的最小插入球的个数。

分析:

问题从小区间推到大区间

因为只有两种颜色(用01表示),我们可以把同色的分成一段一段的,并记录每段内有多少个同色的,这样每个相邻的段一定都是不同色的,相隔的段一定都是同色的。假设用a[i]ia[i]记录第i段内有多少个小球

dp[i][j][i,j]dp[i][j]表示消去区间[i,j],需要插入球的最少的数量

共有一下三种消去方式,一一进行比较然后进行转移即可

1.将区间分成两部分,各自消除自身部分,枚举分隔位置k:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])

2.中间区间全部消去,两端的两端颜色相同,个数大于等于三消去,否则再加1

dp[i][j]=min(dp[i][j],dp[i+1][j1]+(a[i]+a[j]==2))dp[i][j]=min(dp[i][j],dp[i+1][j−1]+(a[i]+a[j]==2))

3.最后一种方式,中间一个球和两端的颜色相同,消去左右两侧后,剩下的三部分相撞消去,枚举中间个数为1且和两端颜色相同的位置k(k+=2)

dp[i][j]=min(dp[i][j],dp[i+1][k1]+dp[k+1][j1])dp[i][j]=min(dp[i][j],dp[i+1][k−1]+dp[k+1][j−1])

此时要求a[i]+a[j]<4a[i]+a[j]<4因为如果大于等于4,一定说明两端的段个数是2(不可能是3+1,因为3不能存在),这样无论我们先删去左边的[i+1,k1][k+1,j1][i+1,k−1]区间还是右边的[k+1,j−1]区间,两端的段一定能和k位置的哪一个凑成三个直接消去,这样就不是三部分相撞消去了,就不是第三种情况了。

除此之外还要注意后两种方式都必须保证[i+1,j-1]的长度是奇数,因为这样才能保证两端是相同的颜色

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 3e2+9;

char s[maxn];
int a[maxn];//记录每段的个数
int dp[maxn][maxn];

int main(){
    int T,cas = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%s",&s);
        int n = strlen(s);
        int cnt = 1;//记录段数
        a[1] = 1;
        for(int i = 1; i < n; i++){
            if(s[i] == s[i-1]) a[cnt]++;
            else a[++cnt] = 1;
        }//每段个数
        for(int len = 0; len <= cnt; len++){
            for(int i = 1; i <= cnt; i++){
                int j = i + len;
                if(j < 1 || j > cnt) continue;
                dp[i][j] = 2 * n;//相当于初始化为无穷大
                if(len == 0)
                    dp[i][j] = max(1,3-a[i]);//每个段至少插入1个,因为三个相同的话肯定消去了不能存在
                else{
                    for(int k = i; k < j; k++){
                        dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j]);//分成左右两部分消去
                    }
                    if((j-i-1) % 2 == 1){//中间有奇数个说明两端的段必定是同种颜色
                        if(a[i] + a[j] == 2){//如果两头的段内个数都是只有一个,那么中间部分消去后还得插入一个,故加一
                            dp[i][j] = min(dp[i][j],dp[i+1][j-1]+1);
                        }
                        else{//否则就不用,因为消去中间之后,两端的段一碰头就消去了
                            dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
                        }
                        if(a[i] + a[j] < 4){//第三种消去方式,中间一个段作为分隔,把除去两端的段和中间这个分隔的段的其他部分消去后,剩下这三部分碰头消去
                            for(int k = i + 2; k < j; k += 2){
                                if(a[k] == 1)
                                    dp[i][j] = min(dp[i][j],dp[i+1][k-1] + dp[k+1][j-1]);
                            }
                        }
                    }
                }
            }
        }
        printf("Case #%d: ",++cas);
        printf("%d\n",dp[1][cnt]);
    }
    return 0;
}
HDU-3480 是一个典型的动态规划问题,其题目标题通常为 *Division*,主要涉及二维费用背包问题或优化后的动态规划策略。题目大意是:给定一个整数数组,将其划分为若干个连续的子集,每个子集最多包含 $ m $ 个元素,并且每个子集的最大值与最小值之差不能超过给定的阈值 $ t $,目标是使所有子集的划分代价总和最小。每个子集的代价是该子集最大值与最小值的差值。 ### 动态规划思路 设 $ dp[i] $ 表示前 $ i $ 个元素的最小代价。状态转移方程如下: $$ dp[i] = \min_{j=0}^{i-1} \left( dp[j] + cost(j+1, i) \right) $$ 其中 $ cost(j+1, i) $ 表示从第 $ j+1 $ 到第 $ i $ 个元素构成一个子集的代价,即 $ \max(a[j+1..i]) - \min(a[j+1..i]) $。 为了高效计算 $ cost(j+1, i) $,可以使用滑动窗口或单调队列等数据结构来维护区间最大值与最小值,从而将时间复杂度优化到可接受的范围。 ### 示例代码 以下是一个简化版本的动态规划实现,使用暴力方式计算区间代价,适用于理解问题结构: ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 10010; int a[MAXN]; int dp[MAXN]; int main() { int T, n, m; cin >> T; for (int Case = 1; Case <= T; ++Case) { cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i]; dp[0] = 0; for (int i = 1; i <= n; ++i) { dp[i] = INF; int mn = a[i], mx = a[i]; for (int j = i; j >= max(1, i - m + 1); --j) { mn = min(mn, a[j]); mx = max(mx, a[j]); if (mx - mn <= T) { dp[i] = min(dp[i], dp[j - 1] + mx - mn); } } } cout << "Case " << Case << ": " << dp[n] << endl; } return 0; } ``` ### 优化策略 - **单调队列**:可以使用两个单调队列分别维护当前窗口的最大值与最小值,从而将区间代价计算的时间复杂度从 $ O(n^2) $ 降低到 $ O(n) $。 - **斜率优化**:若问题满足特定的决策单调性,可以考虑使用斜率优化技巧进一步加速状态转移过程。 ### 时间复杂度分析 原始暴力解法的时间复杂度为 $ O(n^2) $,在 $ n \leq 10^4 $ 的情况下可能勉强通过。通过单调队列优化后,可以稳定运行于 $ O(n) $ 或 $ O(n \log n) $。 ### 应用场景 HDU-3480 的问题模型可以应用于资源调度、任务划分等场景,尤其适用于需要控制子集内部差异的问题,如图像分块压缩、数据分段处理等[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值