[NOIP模拟] Beans (普通祖玛) + DP

10 篇文章 0 订阅
1 篇文章 0 订阅

题目简述 :

    给出一串 01 串,每次可向其中加入 0 或 1, 当有连续 0 或 1 的长度 3 时,该连续串会被抵消, 求使 01 串全部抵消的最小次数。

输入简述 :

    T 代表数据, T个 01 串。

输出简述 :

    所有的答案。

sample input :

4
11
10101
101001001
01001101011001100

sample ouput :

1
4
3
2



PS:其实,我是真的不想水题目,情有可原啊。


题解 & 教训:

    这次考试我炸的那么惨,还是没有策划好时间,先是想了一会儿的 DP, 发现没时间了,又去写搜索,又没有写出来,然后逼迫之下又开始想 DP,然后在离考试结束还有 8 分钟时我想出来了 DP,又有什么用呢,这种策略是十分不成熟的,一定要吸取教训。
    这道题很明显是一个 DP 题,我们再记一个num, 表示压缩后的权值大小,我们压缩是将连续的 0 和 1 压缩成一个整数, 表示连续 0 或 1 的长度, 记 DP[i][j] 表示将 i 到 j 区间全部消掉的最小代价,于是我们可以得到转移方程 :

DP[i][j]=3num[i](i==j)

如果 color[i]!=color[j] , 只能分成两半抵消,转移方程 :
DP[i][j]=DP[i][k]+DP[k+1][j](ik<j)

如果 color[i]==color[j] , 我们有种特殊的情况要讨论, 当 color[i]=color[j]=color[k] 不能直接用两半相加,因为你在抵消其中一半时,已经破坏了另一半的形式 :
DP[i][j]=DP[i+1][k1]+DP[k+1][j1](num[i]+num[j]<4,num[k]=1)

这样列式子主要是为了排除这样的例子 : 1100110010110100110011 答案是 3 不是 2.
color[i]=color[j] 有一个普通的式子 :
DP[i][j]=DP[i+1][j1]+max(0,3num[i]num[j])

代码 :

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
#include <map>
#include <vector>
#define clr(a) memset(a, 0, sizeof(a))
using namespace std;

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(isdigit(ch)) {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 200 + 5;
int dp[MAXN][MAXN], num[MAXN], c[MAXN];

inline void solve() {
    memset(dp, 127, sizeof(dp));
    clr(num);
    char s[MAXN];
    scanf("%s", s + 1);
    int len = strlen(s + 1); int n = 0;
    for(int i = 1; i <= len; ++i) 
        if(s[i] != s[i - 1]) c[++n] = s[i], num[n] = 1;
        else ++num[n];
    for(int i = n; i >= 1; --i) 
        for(int j = i; j <= n; ++j) {
            if(i == j) { dp[i][j] = 3 - num[j]; continue; }
            if(c[i] == c[j]) {
                dp[i][j] = dp[i + 1][j - 1] + max(0, 3 - num[i] - num[j]);
                if(num[i] + num[j] < 4)
                    for(int k = i + 2; k < j; k += 2)
                        if(num[k] == 1)
                            dp[i][j] = min(dp[i][j], dp[i + 1][k -  1] + dp[k + 1][j - 1]);
            }
            for(int k = i; k < j; ++k) dp[i][j] = min(dp[i][k] + dp[k + 1][j], dp[i][j]);
        }
    printf("%d\n", dp[1][n]);
}

int main() {
    int times = read();
    while(times--) {
        solve();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值