【学习笔记】Primal-Dual 原始对偶算法
1.【学习笔记】Kruskal 重构树2.【学习笔记】网络流3.【学习笔记】高级数据结构4.【学习笔记】线性基5.【学习笔记】Link Cut Tree6.【学习笔记】字符串后缀算法7.【学习笔记】字符串回文算法8.【学习笔记】组合数学9.【学习笔记】多项式 1:基础操作10.【学习笔记】多项式 2:集合幂级数11.【学习笔记】多项式 3:多项式运算12.【学习笔记】Prufer 序列13.【学习笔记】多项式 4:生成函数14.【学习笔记】DP 套 DP15.【学习笔记】图的连通性16.【学习笔记】差分约束17.【学习笔记】长链剖分18.【学习笔记】2-SAT19.【学习笔记】根号算法
20.【学习笔记】Primal-Dual 原始对偶算法
21.【学习笔记】Bostan-Mori 算法22.【学习笔记】狄利克雷卷积与高级筛法23.【学习笔记】DP 优化 1:基础优化24.【学习笔记】DP 优化 2:动态 DP25.【学习笔记】李超线段树26.【学习笔记】优化建图27.【学习笔记】Segment Tree Beats28.【学习笔记】插头 DP29.【学习笔记】任意模数多项式乘法30.【学习笔记】SG 函数与 SG 定理31.【学习笔记】类欧几里得算法32.【学习笔记】狄利克雷前/后缀和/差分33.【学习笔记】DSU on Tree34.【学习笔记】DP 优化 3:闵可夫斯基和优化 DP35.【学习笔记】笛卡尔树36.【学习笔记】Miller-Rabin 算法37.【学习笔记】DP 优化 4:决策单调性38.【学习笔记】DP 优化 5:wqs 二分优化 DP39.【学习笔记】边分治40.【学习笔记】KMP 相关算法41.【学习笔记】概率生成函数42.【学习笔记】离散对数和剩余Johnson 全源最短路算法#
Floyd 可以 处理全源最短路,Bellman-Ford 单源最短路的复杂度是 的,Dijkstra 可以做到 但不能处理负边权,所以 Johnson 全源最短路算法通过处理使得可以用 次 Dijkstra 解决有负权图的全源最短路。
先建超级源点,向各点连边权为 的有向边,跑一次 Bellman-Ford,得到最短路 ,将 作为 节点的势能,将 改为 。
这样在最短路过程中,,最短路值应该是:
注意到修改后的最短路较原来只和两端点势能有关,所以按照这个方法去做是可以找到最短路的。
同时根据 ,则 ,也就是一个正权图。
这样复杂度是 。
Primal-Dual 原始对偶算法#
其实和上面类似,这个算法解决了不含负环的费用流,复杂度不再是 EK 的上界 ,其实就是改变了求最短路的算法。
依旧是从源点 开始跑 Bellman-Ford 记录势能 ,边权改为 。
问题在每次增广之后增加并减少了一些边。
这里的解决方案是,每次跑完 Dijkstra,把势能 改为 ,这显然能代表最短路,证明和上面一样,关键是对边权是否全为正的讨论。
证明也是类似的,在上一次跑 Dijkstra 时,原有的边 有 ,于是 ,而新增加的边 的反向边一定在最短路上,于是 ,进而 ,边权非负。
这样就可以在 的复杂度内解决问题,如果图有特殊性质,第一次的 Bellman-Ford 可以换成 BFS 之类的。
点击查看代码
int n,m;
int S,T;
struct edge{
int to,nxt,w,c;
}e[maxm<<1];
int head[maxn],cnt;
inline void add_edge(int u,int v,int w,int c){
e[++cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt,e[cnt].w=w,e[cnt].c=c;
e[++cnt].to=u,e[cnt].nxt=head[v],head[v]=cnt,e[cnt].w=0,e[cnt].c=-c;
}
bool vis[maxn];
ll h[maxn];
queue<int> Q;
inline void SPFA(){
memset(vis,0,sizeof(vis));
memset(h,0x3f,sizeof(h));
vis[S]=true,h[S]=0;
Q.push(S);
while(!Q.empty()){
int u=Q.front();
Q.pop();
vis[u]=false;
for(int i=head[u],v,c;i;i=e[i].nxt){
v=e[i].to,c=e[i].c;
if(!e[i].w) continue;
if(h[u]+c<h[v]){
h[v]=h[u]+c;
if(!vis[v]){
vis[v]=true;
Q.push(v);
}
}
}
}
}
struct Data{
int u;
ll d;
Data()=default;
Data(int u_,ll d_):u(u_),d(d_){}
bool operator<(const Data &rhs)const{
return d>rhs.d;
}
};
priority_queue<Data> PQ;
ll dis[maxn];
int pre[maxn];
inline void Dijkstra(){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[S]=0;
PQ.push(Data(S,0));
while(!PQ.empty()){
int u=PQ.top().u;
PQ.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=head[u],v;i;i=e[i].nxt){
v=e[i].to;
ll c=e[i].c+h[u]-h[v];
if(!e[i].w) continue;
if(dis[u]+c<dis[v]){
pre[v]=i;
dis[v]=dis[u]+c;
PQ.push(Data(v,dis[v]));
}
}
}
}
inline pll MCMF(){
pll res=make_pair(0,0);
SPFA();
while(1){
Dijkstra();
if(dis[T]==llinf) return res;
int mn=inf;
for(int u=T;u!=S;u=e[pre[u]^1].to) mn=min(mn,e[pre[u]].w);
res.fir+=mn;
for(int u=T;u!=S;u=e[pre[u]^1].to){
res.sec+=1ll*e[pre[u]].c*mn;
e[pre[u]].w-=mn,e[pre[u]^1].w+=mn;
}
for(int u=1;u<=n;++u) h[u]+=dis[u];
}
}
作者:SoyTony
出处:https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_Primal-Dual_Algorithm.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
合集:
学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效