会写但是踩坑了的两题

这篇博客探讨了如何使用动态规划解决一个关于项链优化的问题,其中需要确保相邻颜色不相同。博主指出了初始化dp数组的重要性以及处理特殊情况的策略。同时,还介绍了一个平衡字符串的判断问题,关注于字符串中0和1的分布。文章通过实例解释了问题的解决方案,并提到了处理问号字符时的注意事项。
摘要由CSDN通过智能技术生成

前言:不要忘记特判1。。。

值钱的项链

一个环,环上每个点有两种选择,蓝色或红色,都有不同的价值,要求不能两个连着的红色。
很容易想到:

f[i][0] = max(f[i - 1][0], f[i - 1][1]) + v[i][0].val;
f[i][1] = f[i - 1][0] + v[i][1].val;

然后第一个点设置红色和蓝色,跑两次dp就出答案了。

坑点:
蓝(无) 蓝(有) 蓝(有)
红(有) 红(有,很大)
这样第三个蓝如果继承了第二个红,那就出问题了。
有两种方法,一种是dp数组初始化-1e18,另一种是如果要继承的那个值不是-1,那就继承,不然就不继承。
第二种好像会好些,毕竟-1e18容易在某些不为人知的地方溢出(

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
struct node{
    int val, co;
};
vector<node> v[N];
int f[N][2], g[N][2];
void solve()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i <= n; i++) {
        g[i][0] = g[i][1] = -1e18;
        f[i][0] = f[i][1] = -1e18;
        v[i].clear();
    }
    if (m == 0 || n == 0) {
        printf("-1\n");
        return;
    }
    for (int i = 1; i <= n; i++) v[i].resize(m + 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> v[i][j].val;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> v[i][j].co;
        }
    }


    // for (int i = 1; i <= n; i++) {
    //     for (auto j : v[i]) {
    //         cout << j.co << " " << j.val << " ";
    //     }
    //     puts("");
    // }
    for (int i = 1; i <= n; i++) {
        node red, blue;
        red.val = -1, blue.val = -1;
        red.co = 1, blue.co = 0;
        for (int j = 1; j <= m; j++)
        {
            if (v[i][j].co) {
                if (v[i][j].val > red.val) red = v[i][j];
            }
            else {
                if (v[i][j].val > blue.val) blue = v[i][j];
            }
        }
        v[i].clear();
        if (blue.val != -1) v[i].push_back(blue); 
        if (red.val != -1) v[i].push_back(red);
    }

    if (n == 1) {
        if (v[1].size() > 1)
            printf("%lld\n", max(v[1][0].val, v[1][1].val));
        else
            printf("%lld\n", v[1][0].val);
        return;
    }


    bool ok = 1;
    for (int i = 1; i < n; i++) {
        if (v[i].size() == 1 && v[i + 1].size() == 1 && v[i][0].co && v[i + 1][0].co)
            ok = 0;
    }
    if (v[1].size() == 1 && v[n].size() == 1 && v[1][0].co && v[n][0].co)
        ok = 0;

    
    if (v[1].size() && v[1][0].co == 0) {
        f[1][0] = v[1][0].val;
        for (int i = 2; i <= n; i++) {
            if (v[i].size() > 1) {
                f[i][0] = max(f[i - 1][0], f[i - 1][1]) + v[i][0].val;
                f[i][1] = f[i - 1][0] + v[i][1].val;
            }
            else if (v[i][0].co) f[i][1] = f[i - 1][0] + v[i][0].val;
            else f[i][0] = max(f[i - 1][0], f[i - 1][1]) + v[i][0].val;
        }
    }

    if ((v[1].size() && v[1][0].co == 1) || (v[1].size() > 1 && v[1][1].co == 1)){
        if(v[1].size() && v[1][0].co == 1)
            g[1][1] = v[1][0].val;
        if(v[1].size() > 1 && v[1][1].co == 1)
            g[1][1] = v[1][1].val;
        for (int i = 2; i <= n; i++) {
            if (v[i].size() > 1) {
                g[i][0] = max(g[i - 1][0], g[i - 1][1]) + v[i][0].val;
                g[i][1] = g[i - 1][0] + v[i][1].val;
            }
            else if (v[i][0].co) g[i][1] = g[i - 1][0] + v[i][0].val;
            else g[i][0] = max(g[i - 1][0], g[i - 1][1]) + v[i][0].val;
        }
    }

    int ans = max({g[n][0], f[n][0], f[n][1]});
    if (ok)
        printf("%lld\n", ans);
    else
        printf("-1\n");
    for (int i = 1; i <= n; i++) {
        g[i][0] = g[i][1] = -1e18;
        f[i][0] = f[i][1] = -1e18;
        v[i].clear();
    }
}
signed main()
{
    // ios::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);
    int tt;
    cin >> tt;
    while(tt--) solve();
    return 0;
}

平衡的字符串

画了画发现,如果不考虑问号,只包含0和1,那一个串重复很多次的结果,每个长度为k的子串才会0 1都相等。
所以直接判开头长为k的串是否在接下来的时候都出现就好了。
坑点:如果这个长为k的串不够合法,那也是-1,判定他不合法的时候,会有一些问号在后续的遍历中是已经确定的值,不能统计为问号了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5, mod = 1e9 + 7;
char s[N];
signed main()
{
    int n, k;
    cin >> n >> k;
    scanf("%s", s + 1);

    bool ok = 1;

    int cnt0 = 0, cnt1 = 0, cnt2 = 0;
    for (int i = 1; i <= k; i++) {
        int f1 = 0, f2 = 0;
        for (int j = 0; j * k + i <= n; j++) {
            if (s[j * k + i] == '1') f1 = 1;
            else if (s[j * k + i] == '0') f2 = 1;
        }
        if (f1 && f2) ok = 0;
        if (f1) cnt0++;
        else if (f2) cnt1++;
        else cnt2++;
    }
    if (abs(cnt0 - cnt1) > cnt2 || (cnt2 & 1)) ok = 0;
    if (ok) puts("Yes");
    else puts("No");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值