刷题--模拟枚举dp

本文分享了作者在参加ACM竞赛过程中的刷题经历,包括三类题型的解题策略:模拟、枚举和动态规划。对于模拟题,通过短除法优化解决问题;枚举题中,讲述了如何找到洒水器到农田的最长距离;动态规划题则讨论了选取数对以最大化区间和的解题思路。作者反思了在解题过程中遇到的困难和提升的空间。
摘要由CSDN通过智能技术生成

刷题历程,不断进步


前言

题目来源:第一届ACC(AcWing Cup)全国高校联赛(初赛)
题目数量:3
题目难度:递进式,易中难,一道模拟,一道枚举,一道dp

第一题模拟

原名:数圈圈

在这里插入图片描述
在这里插入图片描述
思路:
这是一道典型的模拟类型题
思路点是:短除法
起初的思路是每次输入后用 if 语句判断
后来更优的做法是开一个数组,记录这16个数对应的值

#include <bits/stdc++.h>

using namespace std;

int cnt[16] = {//记录对应数值
    1, 0, 0, 0,
    1, 0, 1, 0,
    2, 1, 1, 2,
    0, 1, 0, 0};

int main()
{
    int n;
    cin >> n;
    if (!n)//特判一下第一个数
        cout << 1 << endl;
    else
    {
        int res = 0;
        while (n)//短除法
        {
            res += cnt[n % 16];
            n /= 16;
        }
        cout << res << endl;
    }
    return 0;
}

反思:没有想到要用数组,而是手写了判断条件
附上初阶代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e7 + 10;

int a[N];

int main()
{
    int x,q;
    cin >> x;
    int res = 0;
    int i = 0;
    q = x;
    while (x > 0)
    {
        if (x > 16)
            a[i] = x % 16;
        else
            a[i] = x;
        i++;
        x = x / 16;
    }
    for (int j = 0; j < i; j++)
    {
        if (a[j] == 0 || a[j] == 4 || a[j] == 6 || a[j] == 9 || a[j] == 10 || a[j] == 13)
            res++;
        else if (a[j] == 8 || a[j] == 11)
            res += 2;
        else
            continue;
    }
    if (q == 0)
        cout << 1 << endl;
    else
        cout << res << endl;

    return 0;
}

第二天枚举

原名:农田灌溉
在这里插入图片描述在这里插入图片描述
这题的做法有很多,这里只是其中一种,枚举的方法。二分也能做,感兴趣的可以自己研究研究

题目意思:找出洒水器到农田的最长距离

做法:记录每两个洒水器到中间农田的时间,找出最久的时间

用一个 max() 函数记录俩俩洒水器之间的农田到洒水器的距离
保持更新max() 函数
特判一下首尾的洒水器

#include <cstdio>
#include <iostream>

using namespace std;

const int N = 210;

int p[N];//洒水器
int n, m;

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n >> m;
        for (int i = 1; i <= m; i++)
            cin >> p[i];
        int res = max(p[1], n - p[m] + 1);
        for (int i = 1; i < m; i++)
            res = max(res, (p[i] + p[i + 1]) / 2 + 1 - p[i]);
        cout << res << endl;
    }
    return 0;
}

反思:没有理清题意,,想不到思路

第三题dp

原名:选取数对

在这里插入图片描述
在这里插入图片描述
题目意思:从数组中选出 k k k 个长度为 m m m不相邻区间,求 k k k个区间求和的最大值

思路:前缀和+dp

闫氏dp分析法:

状态表示

  • f[i][j] 表示在前 i 个数中选取j 个区间的所有集合的结果数目
  • 属性:最大值

状态计算(难点)
这里的 i 指的是右端点
那最后一个区间是确定的,右端点为i,长度为m
那倒数第二个区间的右端点至少是 i-m
依次往前推

判断条件:第i个区间是否选取
if(i - m < 0)
f[i][j] = max(f[i - 1][j],w[i])
else
f[i][j] = max(f[i - 1][j],f[i - m][j - 1] + w[i])
w[i] 是前缀和

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N = 5010;

typedef long long LL;

int n, m, k;
LL s[N], f[N][N];

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) //读取每个数 记录前缀和
    {
        int x;
        scanf("%d", &x);
        s[i] = s[i - 1] + x;
    }

    memset(f, -0x3f, sizeof f); //初始最大化数组

    for (int i = 0; i <= n; i++) //初始化
        f[i][0] = 0;

    for (int j = 1; j <= k; j++)
    {
        LL max_f = 0;
        for (int i = j * m; i <= n; i++)
        {
            max_f = max(max_f, f[i - m][j - 1] + s[i] - s[i - m]);
            f[i][j] = max_f;
        }
    }

    LL res = 0;
    for (int i = 1; i <= n; i++)//这一步可以合并到上面的循环中
        res = max(res, f[i][k]);

    printf("%lld\n", res);
    return 0;
}

反思:题目未读懂+dp不够熟练

加油,努力努力再努力~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tancy.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值