Edu39

1|0D. Timetable

Problem - D - Codeforces

转化成背包问题

dp预处理

很容易看出来是背包,但是怎么把区段内的时间转化成物品很吃力。

首先物品的体积肯定不能直接用题目给的时间点,而是用逃课的数量

然后对物品的价值进行预处理

  • 预处理出每天的日期
  • 每次处理一天留下 k1 的物品之前就先跑一遍找出贡献最小的方案
  • 然后用这个方案来跑背包就行。

数据很小,不会超时

constexpr int N(500 + 10); std::vector<int> sched[N]; int dp[N][N]; void solve() { int n, m, k; std::cin >> n >> m >> k; std::string s; for (int i = 1; i <= n; i++) { std::cin >> s; for (int j = 0; j < m; j++) { if (s[j] == '1') { sched[i].push_back(j);//预处理出每一天的1的日期 } } } memset(dp, 0x3f, sizeof dp); dp[0][0] = 0; for (int i = 1; i <= n; ++i) { int siz(sz(sched[i])); for (int cnt = 0; cnt <= siz; ++cnt) { int res = m;//先求出当前选择cnt个1的能得到的最小贡献 if (cnt) { for (int l = 0, r = cnt - 1; r < siz; ++l, ++r) {//暴力枚举一边所有区间 res = std::min(res, sched[i][r] - sched[i][l] + 1); } } else {//如果是选择0个,那显然贡献就是0 res = 0; } int cntSkip(siz - cnt); for (int l = 0; l + cntSkip <= k; ++l) {//已经转化成了背包问题 dp[i][l + siz - cnt] = std::min(dp[i][l + cntSkip], dp[i - 1][l] + res); } } } std::cout << *std::min_element(dp[n], dp[n] + k + 1) << '\n'; }

2|0E. Largest Beautiful Number

Problem - E - Codeforces

贪心构造和bitmask的妙用

void solve() { #define tests std::string s; std::cin >> s; int n(sz(s)); std::string res; for (int i = 0; i < n - 2; i++) { res += '9'; } for (int lcp = n - 1; lcp >= 0; lcp--) {//从右到左检查,一旦遇到合适的位就输出 if (s[lcp] != '0') { for (char c = s[lcp] - 1; c >= (lcp == 0 ? '1' : '0'); c--) {//枚举该位的所有可能数码 std::string cur = s.substr(0, lcp) + c; int mask = 0;//用位来当数码,标记这一位对应的数码是否出现偶数次 for (int i = 0; i < sz(cur); i++) { mask ^= (1 << (cur[i] - '0')); } std::string me; for (int i = 9; i >= 0; i--) { if ((mask >> i) & 1) { me += (i + '0');//为落单的凑,从大到小凑,尽量接近给的数 } } while (sz(cur) + sz(me) < sz(s)) {//剩下的全部凑9 cur += '9'; } cur += me; if (sz(cur) == sz(s)) { res = cur; break; } } if (sz(res) == sz(s)) { break; } } } std::cout << res << '\n'; }

3|0F. Fibonacci String Subsequences

Problem - F - Codeforces

kmp 原理

矩阵优化DP

3|1思路

考虑每个 F(x) 中与 s 相同的子序列的贡献。设这个子序列为 F(x)p1,F(x)p2,F(x)p3,,F(x)pn

我们想要它成为一个子序列的子串,那么 F(x)[p1,pn] 中除了 p1pn 就不能有别的字符被选。而 [1,p11][pn+1,|F(x)|] 中的每个字符都可以选或不选,相当于每个字符产生 2 的贡献。

如果我们设 fi,j 为 kmp 过程中考虑到 F(x) 的第 i 位,匹配到 s 的第 j 位的答案,那么相当于

fi+1,02fi,0,fi+1,n2fi,n,j[1,n1],fi+1,jfi,j,j[1,n]F(x)i=sj,fi+1,jfi,j1

答案即为 f|F(x)|,n

可以用矩阵刻画这个转移过程。设 FiF(i) 的转移矩阵,那么 Fi=Fi1Fi2​​。

矩阵

题解 P1962 斐波那契数列

在很多问题中,我们在处理递推公式或者动态规划的转移方程时,第 i 项可以由之前的 k 项的值推得,即有:

fi=j=1kaj×fij

构造一个这样的矩阵

A=(0000ak1000ak10100ak20001a1)

则有

(fikfi2,fi1)×A=(fik+1fi1,fi)

即矩阵 k 能把这一段 f 整体右移。

n 远大于 k 时,矩阵能起到显著的加速作用。

如何计算 fn

(f1fk1,fk)×Ank=(fnk+1fn1,fn)

即从 fk 移动到 fn,移动 nk 次,即乘以 nkA​.

以此类推,针对题目构造不同的矩阵

时间复杂度 O(n3x)​​​​​。

3|2代码

constexpr int N(110); int n, m; std::string s; struct mat { Z a[N][N]; mat() { memset(a, 0, sizeof a); } }dp[N]; mat operator * (const mat &a, const mat &b) {//A的列数等于B的行数才可以矩阵乘 mat res; for (int k = 0; k <= n; ++k) { for (int i = 0; i <= n; ++i) { for (int j = 0; j <= n; ++j) { res.a[i][j] += a.a[i][k] * b.a[k][j]; } } } return res; } void solve() { std::cin >> n >> m >> s; dp[0].a[0][0] = dp[0].a[n][n] = dp[1].a[0][0] = dp[1].a[n][n] = 2; for (int i = 1; i < n; i++) { dp[0].a[i][i] = dp[1].a[i][i] = 1; } for (int i = 1; i <= n; i++) { if (s[i - 1] == '0') { dp[0].a[i - 1][i] = 1; } else { dp[1].a[i - 1][i] = 1; } } for (int i = 2; i <= m; i++) { dp[i] = dp[i - 1] * dp[i - 2]; } mat ans; ans.a[0][0] = 1; ans = ans * dp[m]; std::cout << ans.a[0][n] << '\n'; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18088228.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示