同余最短路
1.同余最短路
同于最短路可以理解为完全背包问题扩展,形如:
- 背包总体积非常大,不支持枚举;
- 每个物品的体积比较小,满足
,支持枚举; - 只是判定而不要求最优
注意完全背包,多重背包是做不了的。
但是可以做类多重背包(转移一个等差数列),这个是加很多个等差数列中的几项。
可行性判定转最优性求解应该是优化思路。
考虑对于某一个物品,我们求出以他为模数情况下,剩余系背包的最小体积。
具体的,选定某个物品体积做模数,设为
设
转移是:
可以轻松转化为图论问题然后求最短路即可。
不过有一种更优雅的转圈做法。
当一个体积为
根据著名题目 《最小环》 的结论,会形成
破环成链后,即可使得每个点都可以转移到所有点上,因此等价于转两圈即可,给出模板:
for(int i=1,x;i<n;i++)
{
cin>>x;
for(int j=0,lim=__gcd(x,m);j<lim;j++)
{
for(int k=j,c=0;c<2;c+=(k==j),k=(k+x)%m)
{
int t=(k+x)%m;
f[t]=min(f[t],f[k]+x);
}
}
}
Tricks
-
转两圈可以变成转一圈,注意到环上初始代价最小的点一定不会被更新,因此以他为起点一定正确。
-
对于最优化问题,可以考虑一定的贪心策略,将基数特殊化
考虑对比两组背包方案的优劣是具体的写出每个背包方案的估价函数。
在转移的时候更新加入一次该物品造成的代价。
- 更换基数:
先将dp值换模数,具体而言就是
然后再将原来的基数作为一个物品进行转移:
点击查看代码
void trans(int x)
{
memcpy(g,f,sizeof f);
for(int j=0;j<x;j++) f[j]=Inf;
for(int j=0;j<B;j++) f[g[j]%x]=min(f[g[j]%x],g[j]);
for(int j=0,lim=__gcd(x,B);j<lim;j++)
for(int k=j,c=0;c<2;k=(k+B)%x,c+=(k==j))
f[(k+B)%x]=min(f[(k+B)%x],f[k]+B);
B=x;
}
- 转移等差数列
首先将基数改成首项,依然是对每一个转移环依次处理,然后在只增加一个首项的情况下可以从第
设从环上第
注意此时找到环上最小值作为起点再转移更方便。
点击查看代码
void update(int x,int d,int len)
{
trans(x);
if(!d) return;
for(int j=0,lim=__gcd(x,d);j<lim;j++)
{
int Minp=j,tot=0;
for(int k=j,c=0;c<1;k=(k+d)%x,c+=(k==j)) if(f[k]<f[Minp]) Minp=k;
for(int k=Minp,c=0;c<1;k=(k+d)%x,c+=(k==Minp)) seq[++tot]=k;
int hh=0,tt=-1;
q[++tt]=1;
for(int i=2;i<=tot;i++)
{
while(hh<=tt && q[hh]<i-len) hh++;
if(hh<=tt) f[seq[i]]=min(f[seq[i]],f[seq[q[hh]]]+(i-q[hh])*d+x);
while(hh<=tt && f[seq[i]]-i*d <= f[seq[q[tt]]]-q[tt]*d) tt--;
q[++tt]=i;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】