同余最短路
- 题型大概为若干个无限多个正整数来拼凑出某些数(类似完全背包)。
- 套路为随便选一个数 转化为求 表示拼凑出 的最小 。转化为最短路。
- 该模型的强大之处在于将完全背包的结构清晰地展现出来。
同余最短路求能凑成多少(H以内的)数#
经典题目:跳楼机
随便挑一个数 作为模数,求出上述 即可算出答案。
inline void dij() {
...
while (!q.empty()) {
...
to = (cur + y) % x;
if (vis[to]) continue;
if (dis[cur] + y < dis[to]) {
dis[to] = dis[cur] + y;
q.push((node){dis[to], to});
}
...
}
}
同余最短路求最大不能拼凑出来的数#
“小凯的疑惑”加强版
经典题目:牛场围栏
特判掉存在 的情况和 gcd 不为 1 的情况。
随便找一个数(最小的数)当作模数,求出 后根据 求解。
绕圈法#
我退役后才注意到的新做法。将同余最短路看作完全背包问题,最多加 个 ,否则可以被 替换掉。因此可以对 上由加 得到的 个轨道分别顺序递推(例如对每个轨道找到最小值,从这里开始往后更新;或者直接推两遍)。复杂度是优秀的 且极其好写。
memset(f, 0x3f, sizeof(f));
f[0] = 0;
for (int i = 2; i <= n; ++i) {
int v = a[i], g = get_gcd(a[1], a[i]);
for (int j = 0; j < g; ++j) {
int p = j;
do {
MIN(f[(p+v)%a[1]], f[p]+v);
p = (p+v)%a[1];
} while (p != j);
do {
MIN(f[(p+v)%a[1]], f[p]+v);
p = (p+v)%a[1];
} while (p != j);
}
}
一类完全背包问题#
题意:
本题中,你需要解决完全背包问题。
有 种物品,第 种物品单个体积为 、价值为 。
次询问,每次给出背包的容积 ,你需要选择若干个物品,每种物品可以选择任意多个(也可以不选),在选出物品的体积的和恰好为 的前提下最大化选出物品的价值的和。你需要给出这个最大的价值和,或报告不存在体积和恰好为 的方案。
为了体现你解决 NP-Hard 问题的能力, 会远大于 ,详见数据范围部分。
题解:
直观想法是尽可能多去选性价比最高的物品(设其为 ),问题是怎么样尽早达成余数要求,并且这里未必“越早越好”,还需要考虑达成余数要求时所产生的那部分额外收益。
考虑模 下跑同余最短路时,对于两个余数相同的状态 和 ,假设后来有个询问 满足 ,我们将会选择 且 最大的状态。因此可以把 作为状态的权值来比较更新。(后面会证明这样得到的答案必定满足 )
基于上述思想,同余最短(长)路转移时可以通过加入 物品由 转移到 ,权值更新: f[q]=max(f[q], f[p]+c[i]-(p+v)/m*w)
,即跑最长路。因为 性价比最高,所以图无正环。
因为无正环,所以最长路无环,所以最多 条边,即最多用了 个物品,因此最多体积 ,即 ,所以这样得到的答案必定满足 。
单调队列优化等差数列同余最短路#
使用字符串 的所有 border 长度能拼出来 以内的多少个数?
border 长度集合可以划分为不超过 个等差数列。对于等差数列 ,在 下跑同余最短路,则转移的时候是在每个轨道上用长度为 滑动窗口取最小值来转移。如何从模 切换到模 ?注意到我们处理出来的模 的结果告诉我们长度集合 是可以达到的,因此在模 下我们可以先把 都加上去,然后再用 转移一遍。
当然还有一些奇奇怪怪的同余最短路题
Small Multiple#
给定 ,求 的倍数中,数位和最小的那一个数的数位和。
Small Multiple 题解#
- 每个十进制数都可以由“乘十”和“加一”来表示,其中“加一”的次数即为数位和。
- 因为要求 的倍数,即模 为0,因此考虑在模 下的从 到 的最短路。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】