牛客寒假训练营第三场

B

题目描述:

智乃来到水果摊前买瓜,水果摊上贩卖着N个不同的西瓜,第i个西瓜的重量为wi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi2。也就是说对于每个西瓜,智乃都有三种不同的决策:

1.购买一整个重量为wi的西瓜;2.把瓜劈开,购买半个重量为wi2的西瓜;3.不景行购买操作。

为了简化题目,我们保证所有瓜的重量都是一个正偶数。现在智乃想要知道,如果它想要购买西瓜的重量和分别为k=1,2,3M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对109+7取余数后的结果。

分析:

​ 首先我们要求的是方案数,题目种给了买瓜的决策,要么不买西瓜,要么买一半的西瓜,要么买一整个西瓜,我们很容易可以想到01背包DP模型来进行求解,不妨开一个dp[N][M]的二维数组。

​ 每一次的状态都是由三个决策组成的,也就是当我们选到第i个物品的时候,此时的重量是j,那么dp[i][j]表示的方案数就是由dp[i1][j]这个不选择这个当前瓜的方案加上dp[i1][jw]买一整个瓜的决策再加上dp[i1][jw2]买半个瓜的决策构成的这样我们就推出来了很经典的01背包的状态转移方程dp[i][j]=dp[i1][j]+dp[i1][jw]+dp[i1][jw2]。当然我们也可以用滚动数组来进行优化,从而少掉一维,最终的转移方程就是dp[j]=dp[jw]+dp[jw2]

#include <bits/stdc++.h> using i64 = long long; constexpr int mod = 1e9 + 7; int main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, m; std::cin >> n >> m; std::vector<int> dp(m + 1); dp[0] = 1; for (int i = 1; i <= n; i ++ ) { int x; std::cin >> x; for (int j = m; j >= x / 2; j -- ) { dp[j] = (dp[j] + dp[j - x / 2]) % mod; if (j >= x) dp[j] = (dp[j] + dp[j - x]) % mod; } } for (int i = 1; i <= m; i ++ ) std::cout << dp[i] << " \n"[i == m]; }

D

题目描述:

智乃有一个长度为N仅有字符01组成的字符串,现在智乃想把这个01串打乱,变得和之前不一样。请你将这个01串打乱后输出。
打乱指的是,打乱后的01串和原本的01串中字符01的数目相同,字符串长度相同,且至少存在一个位置上的字符不同。输入保证其中字符0和1至少会出现一次。

分析:

这个题的思路很简单,只要找到某一个0和1的位置然后将两个字符交换位置就好了,但是参考Jiangly大佬的代码,我们交换字符的时候不需要很朴素的借用中间变量来实现,可以用异或的性质来巧妙的实现,这样我们可以很快速的求出和s[0]不一样的另一个字符所在的位置,然后我们直接用swap函数来交换就可以了。

#include <bits/stdc++.h> using i64 = long long; int main() { std::string s; int n; std::cin >> n >> s; int x = s.find(s[0] ^ '0' ^ '1'); std::swap(s[0], s[x]); std::cout << s << "\n"; }

I

题目描述:

智乃去注册账号,他发现网站的的密码必须符合以下几个条件:

  • 密码的长度不少于L个字符,并且不多于R个字符。

  • 密码是仅包含大小写英文字母、数字、特殊符号的字符串。

  • 密码中应该至少包括①大写英文字母、②小写英文字母、③数字、④特殊符号这四类字符中的三种。

    所谓特殊字符,是指非大小写字母、数字以及空格回车等不可见字符的可见字符。现在智乃有一个长度大小为N的字符串S,她想知道S串中有多少个子串是一个符合条件的密码,请你帮助智乃统计符合条件的密码数目。子串是指字符串中某一段连续的区间,例如对于字符串"abcde"来说,"abc","cde"都是它的子串,而"ace"不是它的子串。

分析:

​ 因为我们要找的区间的长度是有规定且在一定范围内的,所以我们对于这种题总可以发现我们可以固定一个端点,然后再去找右端点的位置,这样很明显右端点是单调的,对于这种问题我们可以想到双指针算法来确定左右边界从而达到我们想要的结果。小技巧:在判断一个字符是大小写还是数字还是其他字符的时候可以用一些函数来帮我们实现,就比如std::isupprtstd::islowerstd::isdigit来快速判断类型,可以帮我们节约一点写代码的时间。

​ 在双指针进行确定左右边界的时候我们一定要记住在滑动这个窗口的时候,要记得把更新之后加入到窗口的元素加进来,更新出去的元素剔除出去。

#include <bits/stdc++.h> using i64 = long long; void solve() { int n, l, r; std::cin >> n >> l >> r; std::string s; std::cin >> s; std::vector<int> a(n + 1); for (int i = 0; i < n; i++ ) { if (std::isupper(s[i])) a[i] = 0; else if (std::islower(s[i])) a[i] = 1; else if (std::isdigit(s[i])) a[i] = 2; else a[i] = 3; } int cnt[4] = {}; i64 res = 0; for (int i = 0, j = 0; i < n; i ++ ) { while (j <= n && (cnt[0] > 0) + (cnt[1] > 0) + (cnt[2] > 0) + (cnt[3] > 0) < 3) { if (j < n) cnt[a[j]] ++ ; j ++; } int L = std::max(l + i, j); int R = std::min(n, i + r); res += std::max(0, R - L + 1); cnt[a[i]] --; } std::cout << res << "\n"; } int main() { std::cin.tie(nullptr)->sync_with_stdio(false); int T; T = 1; while (T--) solve(); return 0; }

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16140273.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示