网络流23题做题笔记

news/2025/1/7 20:29:03/文章来源:https://www.cnblogs.com/Iictiw/p/17629924.html

link

【模板】网络最大流
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=1e5+5;
const ll inf=1e16+5;
int n,m,S,T,head[N],idx=1;
struct edge{int to,next;ll val;}e[N<<1];
inline void add(int from,int to,ll val){e[++idx]={to,head[from],val};head[from]=idx;e[++idx]={from,head[to],0};head[to]=idx;
}
int dep[N],cur[N];
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;dep[S]=1,q.push(S);cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){q.push(to),dep[to]=dep[p]+1,cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;ll c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;ret+=c,flow-=c;}return ret;
}
ll res;
void dinic(){while(bfs()){res+=dfs();}}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m>>S>>T;for(int i=1,u,v,w;i<=m;i++){cin>>u>>v>>w;add(u,v,w);}dinic();cout<<res<<endl;return 0;
}
【模板】最小费用最大流
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=5e3+5,M=5e4+5;
const ll inf=1e16+5;
int n,m,S,T,idx=1,head[N];
struct edge{int to,next;ll val,cst;}e[M<<1];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncost;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;dis[S]=0,q.push(S),cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0,cur[p]=head[p];for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst;if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return vis[p]=0,ret;
}
void MCMF(){while(spfa()){ll f=dfs();mxflow+=f,mncost+=f*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m>>S>>T;for(int i=1,u,v,w,c;i<=m;i++)cin>>u>>v>>w>>c,add(u,v,w,c);MCMF();cout<<mxflow<<' '<<mncost<<endl;return 0;
}

餐巾计划问题

线性规划模型。

将每天拆为白天和晚上两个点,在白天存储所有干净餐巾,在晚上存储所有脏餐巾。对于每一天 \(i\),由第 \(i\) 个白天向汇点连流量为 \(r_i\),费用为 \(0\) 的边,通过限定最大流以满足对餐巾数量的要求。由源点向第 \(i\) 个晚上连流量为 \(r_i\),费用为 \(0\) 的边,表示第 \(i\) 天后多了 \(r_i\) 个脏餐巾。由第 \(i\) 天晚上向第 \(i+1\) 天晚上连流量为 \(\inf\),费用为 \(0\) 的边,表示可以将任意块餐巾留到第二天晚上。由源点向第 \(i\) 个白天连流量为 \(\inf\),费用为 \(p\) 的边,表示每天可以买任意块干净餐巾。由第 \(i\) 天晚上向第 \(i+m\) 天连流量为 \(\inf\),费用为 \(f\) 的边,由第 \(i\) 天晚上向第 \(i+n\) 天晚上连流量为 \(\inf\),费用为 \(s\) 的边,表示可以在第 \(i\) 天快洗或慢洗任意多条餐巾。此时的最小费用最大流即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=5005,M=5e5+5;
const ll inf=1e16+5;
int n,a[N],p,x,f,y,s;
int S,T,head[N],idx=1;
struct edge{int to,next;ll val,cst;}e[M<<1];
inline void add(int from,int to,ll val,ll cst){e[++idx]=(edge){to,head[from],val,cst},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cst},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(int i=1;i<=n;i++)cin>>a[i];cin>>p>>x>>f>>y>>s;S=0,T=2*n+1;for(int i=1;i<=n;i++)add(S,i,inf,p);for(int i=1;i<=n;i++)add(i,T,a[i],0);for(int i=1;i<=n;i++)add(S,i+n,a[i],0);for(int i=1;i<n;i++)add(i+n,i+n+1,inf,0);for(int i=1;i+x<=n;i++)add(i+n,i+x,inf,f);for(int i=1;i+y<=n;i++)add(i+n,i+y,inf,s);dinic();cout<<mncst<<endl;return 0;
}

星际转移问题

分层最大流。

将点按天数分层,对于第 \(t\) 天,若太空船 \(k\) 由点 \(i\) 驶往点 \(j\),则由 \(t-1\) 张图上的点 \(i\) 向第 \(t\) 张图上的点 \(j\) 连流量为 \(h_k\) 的边,表示可以由 \(i\)\(j\) 转移 \(h_k\) 个人;同时,由第 \(t-1\) 张图上的第 \(i\) 个点向第 \(t\) 张图上的第 \(i\) 个点连流量为 \(\inf\) 的边,表示第 \(i\) 个点上的人留在原处。枚举天数 \(t\),将新图建出后,若此时最大流不小于 \(k\),则 \(t\) 为答案。

可以证明本题答案的一个上界是 \((n-1+k)r\) (在本题中不大于 \(1000\)),因此当 \(t\) 超过上界时,即可判定无解。

建图时要注意对重复点与源汇点的特殊处理。

code
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int N=20005,inf=1e9+5;
int n,m,k,h[N],r[N];vector<int>v[N];
inline int pos(int t,int i){return v[i][t%r[i]];}
inline int id(int t,int i){return t*n+pos(t,i);}
int S,T,head[N],idx=1;
struct edge{int to,next,val;}e[N<<5];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int dep[N],cur[N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m>>k;for(int i=1;i<=m;i++){cin>>h[i]>>r[i],v[i].resize(r[i]);for(int j=0;j<r[i];j++)cin>>v[i][j];}S=N-1,T=N-2;for(int t=1;t<=1000;t++){for(int i=1;i<=n;i++)add((t-1)*n+i,t*n+i,inf);for(int i=1;i<=m;i++){if(pos(t,i)==pos(t-1,i))continue;if(pos(t,i)==-1){if(pos(t-1,i)==0)add(S,T,h[i]);else add(id(t-1,i),T,h[i]);}else if(pos(t-1,i)!=-1&&pos(t,i)!=0){if(v[i][(t-1)%r[i]]==0)add(S,id(t,i),h[i]);else add(id(t-1,i),id(t,i),h[i]);}}dinic();if(res>=k)return cout<<t<<endl,0;}cout<<0<<endl;return 0;
}

飞行员配对方案问题

二分图最大匹配。

视外籍飞行员为左部点,英国飞行员为右部点,源点向每个左部点连流量为 \(1\) 的边,每个右部点向汇点连流量为 \(1\) 的边,左部点向能匹配的右部点连流量为 \(1\) 的边,此时网络的最大流即为答案。

输出方案时,在残量网络中找左部点向右部点连的流满的边,即为满足条件的一组方案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=305,inf=1e9+5;
int n,m,S,T,idx=1,head[N];
struct edge{int to,next,val;}e[N*N<<1];
inline void add(int from,int to,int val){e[++idx]={to,head[from],val},head[from]=idx;e[++idx]={from,head[to],0},head[to]=idx;
}
int dep[N],cur[N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>m>>n;int tot=0;while(1){int u,v;cin>>u>>v;if(u==-1)break;add(u,v,1),tot++;}S=0,T=n+1;for(int i=1;i<=m;i++)add(S,i,1);for(int i=m+1;i<=n;i++)add(i,T,1);dinic();cout<<res<<'\n';for(int i=1;i<=tot;i++)if(!e[i*2].val)cout<<e[i*2+1].to<<' '<<e[i*2].to<<'\n';return 0;
}

软件补丁问题

最短路。

对与每个状态,向其被补丁处理后的状态连边,跑最短路。

由于图边数很多,但实际转移会用到的很少,因此可以在转移到对应点时再处理相应的边。

混进网络流的最短路*1。

code
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=21,M=105,inf=1e9+5;
int n,m;
int val[M],b1[M],b2[M],f1[M],f2[M];
int dis[1<<N];bool vis[1<<N];
struct node{int p,v;friend bool operator<(node x,node y){return x.v>y.v;}
};
priority_queue<node>q;
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;for(int i=1;i<=m;i++){string s,t;cin>>val[i]>>s>>t;for(int j=0;j<n;j++){if(s[j]=='+')b1[i]|=1<<j;if(s[j]=='-')b2[i]|=1<<j;if(t[j]=='+')f2[i]|=1<<j;if(t[j]=='-')f1[i]|=1<<j;}}for(int i=0;i<1<<n;i++)dis[i]=inf;q.push((node){(1<<n)-1,dis[(1<<n)-1]=0});while(q.size()){int p=q.top().p;q.pop();if(p==0)return cout<<dis[p]<<endl,0;if(vis[p])continue;vis[p]=1;for(int i=1;i<=m;i++){if((p|b1[i])!=p||(p&b2[i])!=0)continue;int to=((p|f2[i])|f1[i])-f1[i];if(dis[to]>dis[p]+val[i]){dis[to]=dis[p]+val[i];q.push((node){to,dis[to]});}}}cout<<0<<endl;return 0;
}

太空飞行计划问题

最大权闭合子图。

闭合子图:给定一个有向图,从中选择零或多个点组成点集 \(V\),对于 \(V\) 中任意点 ,满足其所有后继节点也在 \(V\) 中。

最大权闭合子图:点有点权,权值和最大的闭合子图。

求最大权闭合子图,将源点向所有点权为正的点连边,流量为点权;所有点权为负的点向汇点连边,流量为点权的相反数;权值为 \(0\) 的点不做处理;所有原图上的边流量为 \(\inf\) 。此时最大权闭合子图的权值等于所有正权点的权值减去最小割。

证明

Never gonna give you up

输出最小割方案时,输出所有在残量网络上与源点连通的点即可。对于 dinic 算法来说,仅需输出最后一轮增广后仍有深度的点即可。

code
#include<iostream>
#include<cstdio>
#include<sstream>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=505;
const ll inf=1e16+5;
int n,m,a[N],b[N];
int S,T,head[N],idx=1;
struct edge{int to,next;ll val;}e[N*N<<1];
inline void add(int from,int to,ll val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int dep[N],cur[N];ll res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;ll c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;S=0,T=n+m+1;ll num=0;for(int i=1,x;i<=n;i++){string ln;cin>>ws,getline(cin,ln);istringstream in(ln);in>>a[i],num+=a[i];add(S,i,a[i]);while(in>>x)add(i,n+x,inf);}for(int i=1;i<=m;i++)cin>>b[i],add(n+i,T,b[i]);dinic();for(int i=1;i<=n;i++)if(dep[i])cout<<i<<' ';cout<<'\n';for(int i=1;i<=m;i++)if(dep[i+n])cout<<i<<' ';cout<<'\n';cout<<num-res<<'\n';return 0;
}

试题库问题

二分图匹配。

源点向所有试题连边边权为 \(1\) 的边,每个试题向其所有的类别连边权为 \(1\) 的边,类别 \(i\) 向汇点连权值为 \(m_i\) 的边。若最大流等于 \(m\) ,则存在合法方案。

输出方案时,在残量网络上寻找试题向类别之间流满的边,即为一组合法方案。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e3+5,inf=1e9+5;
int k,m,n,a[N];
inline int get(int i,int j){return (i-1)*m+j;}
int S,T,head[N*N],idx=1;
struct edge{int to,next,val;}e[N*N<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int dep[N*N],cur[N*N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
vector<int>ans[N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>k>>n;S=0,T=k+n+1;for(int i=1,x;i<=k;i++){cin>>x;m+=x;add(n+i,T,x);}for(int i=1,k,x;i<=n;i++){add(S,i,1);cin>>k;while(k--)cin>>x,add(i,n+x,1);}dinic();if(res!=m)return cout<<"No Solution!"<<endl,0;for(int p=1;p<=n;p++)for(int i=head[p];i;i=e[i].next)if(e[i].to>0&&!e[i].val)ans[e[i].to-n].push_back(p);for(int i=1;i<=k;i++){cout<<i<<":";for(auto j:ans[i])cout<<' '<<j;cout<<endl;}return 0;
}

最小路径覆盖问题

DAG 最小路径覆盖。

先将原图用 \(n\) 条路径覆盖,每条路径仅经过一个点。此时尝试将首尾相连的路径合并,最小路径覆盖数就等于总点数减去最大合并数。

将每个点拆为入点和出点,对于 DAG 上的每条边,由对应的入点向对应的出点连边,此时形成的二分图的最大匹配即为最大合并数。

输出方案时,在残量网络上寻找入点向出点满流的边,这条边对应着一组被合并的路径,记录每个点对应的下一个点即可。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=155,M=6005,inf=1e9+5;
int n,m;
int S,T,head[N*2],idx=1;
struct edge{int to,next,val;}e[M<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int dep[N*2],cur[N*2],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int ne[N];bool vis[N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;S=0,T=2*n+1;for(int i=1,u,v;i<=m;i++)cin>>u>>v,add(u,v+n,1);for(int i=1;i<=n;i++)add(S,i,1),add(i+n,T,1);dinic();for(int p=1;p<=n;p++)for(int i=head[p];i;i=e[i].next)if(!e[i].val&&e[i].to)ne[p]=e[i].to-n,vis[ne[p]]=1;for(int i=1;i<=n;i++){if(vis[i])continue;int p=i;while(p)cout<<p<<' ',p=ne[p];cout<<'\n';}cout<<n-res<<'\n';return 0;
}

魔术球问题

DAG 最小路径覆盖。

将和为完全平方数的数对连边(小数向大数连),原问题相当于最小路径覆盖不大于 \(n\) 时最多加入多少点。考虑将点由小到大加入图中,每次在上一轮参量网络的基础上加入新边计算最小路径覆盖。最小路径覆盖超过 \(n\) 时,上一次加入的数即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int N=7005,inf=1e9+5;
int n,S,T,idx=1,head[N];
struct edge{int to,next,val;}e[N<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int cur[N],dep[N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){q.push(to),dep[to]=dep[p]+1,cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(flow,e[i].val));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int ne[N];bool vis[N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;int m=3500;S=0,T=N-1;for(int i=1;;i++){add(S,i,1),add(i+m,T,1);for(int j=1;j<i;j++)if((int)sqrt(i+j)*(int)sqrt(i+j)==i+j)add(j,i+m,1);dinic();if(i-res>n){cout<<i-1<<'\n';for(int p=1;p<i;p++)for(int i=head[p];i;i=e[i].next)if(!e[i].val&&e[i].to>0)ne[p]=e[i].to-m,vis[e[i].to-m]=1;for(int p=1;p<i;p++){if(vis[p])continue;int i=p;while(i)cout<<i<<' ',i=ne[i];cout<<'\n';}return 0;}}return 0;
}

最长不下降子序列问题

分层最大流。

第一问答案易得,同时我们可以求出以第 \(i\) 个数为结尾的最长不下降子序列长度 \(f_i\)。设第一问答案为 \(s\)

对于第二问,考虑进行网络流建模。因为每个数至多选一次,因此将每个点拆为入点和出点,从入点向出点连一条流量为 \(1\) 的边。

由于要求长度最长,可以证明每个数在可能的最长不下降子序列中的位置是固定的(即 \(f_i\))。因此,将各个数按 \(f_i\) 的大小分类后建图。对于两个数 \(i,j(i \lt j)\) ,若可以从 \(i\) 转移至 \(j\) (即 \(a_i \leq a_j \wedge f_i + 1 = f_j\)),则由点 \(i\) 的出点往点 \(j\) 的入点连一条流量为 \(1\) 的边。若 \(i\) 满足 \(f_i = 1\),则由源点向点 \(i\) 的入点连一条流量为 \(1\) 的边;若 \(i\) 满足 \(f_i = s\),则由点 \(i\) 的出点向汇点连一条流量为 \(1\) 的边。此时图中的最大流即为最多可取出的最长不下降子序列个数。

对于第三问,仅需去掉网络中对 \(1\)\(n\) 的限制:将点 \(1\) 和点 \(n\) 入点向出点的连边的流量改为 \(\inf\),将源点向点 \(1\) 的入点的流量改为 \(\inf\) (因为 \(f_1\) 一定等于 \(1\),所以源点向点 \(1\) 一定有边),若 \(f_n = s\),则将点 \(n\) 的出点向汇点的流量改为 \(\inf\)。此时的最大流即为第三问答案。注意特判 \(n=1\) 的情况。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=1005,inf=1e9+5;
int n,a[N];
int S,T,idx=1,head[N];
struct edge{int to,next,val;}e[N*N<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int cur[N],dep[N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){q.push(to),dep[to]=dep[p]+1,cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();};
int f[N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;if(n==1)return cout<<1<<endl<<1<<endl<<1<<endl,0;for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=n;i++)for(int j=0;j<i;j++)if(a[j]<=a[i])f[i]=max(f[i],f[j]+1);int ans=0;for(int i=1;i<=n;i++)ans=max(ans,f[i]);cout<<ans<<endl;S=0,T=2*n+1;for(int i=1;i<=n;i++){add(i,i+n,1);if(f[i]==1)add(S,i,1);if(f[i]==ans)add(i+n,T,1);for(int j=1;j<i;j++)if(a[j]<=a[i]&&f[j]+1==f[i])add(j+n,i,1);}dinic();cout<<res<<endl;add(S,1,inf);add(1,1+n,inf);add(n,n+n,inf);if(f[n]==ans)add(n+n,T,inf);dinic();cout<<res<<endl;return 0;
}

航空路线问题

限制性带权路径。

可以将原问题转化为寻找两条互不相交的由 \(1\)\(n\) 的路径,满足路径长度和最大。将每个点拆为入点和出点,每个入点向对应的出点连一条流量为 \(1\),费用为 \(0\) 的边;对于原图中的边 \((u,v)\),由点 \(u\) 的出点向点 \(v\) 的入点连一条流量为 \(1\),费用为 \(1\) 的边;由源点向点 \(1\) 的出点连一条流量为 \(2\),费用为 \(0\) 的边,由点 \(n\) 的入点向汇点连一条流量为 \(2\),费用为 \(0\) 的边。

此时,若最大流为 \(2\),则最大费用最大流即为第一问答案;若最大流为 \(1\),而最大费用最大流也为 \(1\),则第一问答案为 \(2\) (存在形如 \(1 \to n \to 1\)) 的路径;否则,输出无解。

输出方案时,由点 \(1\) 开始经过满流的边进行两遍 dfs。第一遍 dfs 正序输出,第二遍 dfs 寻找未经过的点并倒序输出。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
const int N=305,inf=1e9+5;
int n,m,tot;
map<string,int>mp;string s[N];
int S,T,idx=1,head[N];
struct edge{int to,next,val,cst;}e[N*N<<3];
inline void add(int from,int to,int val,int cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
int dis[N],cur[N];bool vis[N];
int mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])vis[to]=1,q.push(to);}}}return dis[T]<inf;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;vis[p]=1;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;int c=dfs(to,min(flow,e[i].val));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){int res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
void dfs1(int p){cout<<s[p]<<endl;vis[p]=1;for(int i=head[p+n];i;i=e[i].next)if(!e[i].val&&e[i].to<=n&&!vis[e[i].to])return dfs1(e[i].to);
}
void dfs2(int p){for(int i=head[p+n];i;i=e[i].next)if(!e[i].val&&e[i].to<=n&&!vis[e[i].to])dfs2(e[i].to);cout<<s[p]<<endl;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++)cin>>s[i],mp[s[i]]=++tot;for(int i=1;i<=m;i++){string s,t;cin>>s>>t;int u,v;u=mp[s],v=mp[t];if(u>v)swap(u,v);add(u+n,v,1,-1);}S=0,T=2*n+1;for(int i=1;i<=n;i++)add(i,i+n,1,0);add(S,n+1,2,0);add(n,T,2,0);dinic();if(mxflow!=2){if(mxflow==1&&mncst==-1){cout<<2<<endl;cout<<s[1]<<endl<<s[n]<<endl<<s[1]<<endl;return 0;}return cout<<"No Solution!"<<endl,0;}cout<<-mncst<<endl;for(int i=0;i<=n;i++)vis[i]=0;dfs1(1),dfs2(1);return 0;
}

方格取数问题

二分图最大独立集。

将原图黑白染色,将相邻的白点与黑点之间连边,原问题即为求二分图最大权独立集。

二分图最大权独立集即为所有点点权总和减去最小点权覆盖。源点向所有黑点连边权为黑点点权的边,所有白点向汇点连边权为白点点权的边,黑点向相邻的白点连边权为 \(\inf\) 的边,此时的最小割即为最小点权覆盖。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=105,inf=1e9+5;
int n,m,a[N][N];
inline int get(int i,int j){return (i-1)*m+j;}
int S,T,head[N*N],idx=1;
struct edge{int to,next,val;}e[N*N<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int dep[N*N],cur[N*N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;int tot=0;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j],tot+=a[i][j];S=0,T=n*m+1;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if((i+j)&1){add(S,get(i,j),a[i][j]);if(i>1)add(get(i,j),get(i-1,j),inf);if(i<n)add(get(i,j),get(i+1,j),inf);if(j>1)add(get(i,j),get(i,j-1),inf);if(j<m)add(get(i,j),get(i,j+1),inf);}else add(get(i,j),T,a[i][j]);}}dinic();cout<<tot-res<<endl;return 0;
}

圆桌问题

二分图匹配。

将单位视为左部点,桌子视为右部点。源点向单位 \(i\) 连流量为 \(r_i\) 的边,桌子 \(j\) 向汇点连流量为 \(c_j\) 的边,每个单位向每个椅子连流量为 \(1\) 的边,若最大流等于代表人数,则有合法方案。

输出方案时,在残量网络上寻找单位与椅子间流满的边,即为选择方案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int N=505,inf=1e9+5;
int n,m,r[N],c[N],tot;
int S,T,head[N*3],idx=1;
struct edge{int to,next,val;}e[N*N*4];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int cur[N],dep[N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(flow,e[i].val));if(!c)dep[to]=0;e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
vector<int>ans[N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;for(int i=1;i<=n;i++)cin>>r[i],tot+=r[i];for(int i=1;i<=m;i++)cin>>c[i];S=0,T=n+m+1;for(int i=1;i<=n;i++)for(int j=n+1;j<=n+m;j++)add(i,j,1);for(int i=1;i<=n;i++)add(S,i,r[i]);for(int i=n+1;i<=n+m;i++)add(i,T,c[i-n]);dinic();if(res!=tot)return cout<<0<<endl,0;cout<<1<<endl;for(int p=1;p<=n;p++)for(int i=head[p];i;i=e[i].next)if(e[i].to>0&&!e[i].val)ans[p].push_back(e[i].to-n);for(int i=1;i<=n;i++){for(auto j:ans[i])cout<<j<<' ';cout<<endl;}return 0;
}

骑士共存问题

二分图最大独立集。

将原图黑白染色,若将黑格视为左部点,白格视为右部点,能互相攻击到的格子之间连边,则会得到一个二分图,此时二分图的最大独立集即为答案。

二分图最大独立集即为总点数减去最小点覆盖数,将原点向所有黑格连流量为 \(1\) 的边,所有白格向汇点连流量为 $1 $ 的边,黑点向能攻击到的白点连流量为 \(\inf\) 的边,此时的最小割即为最小点覆盖数。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=205,inf=1e9+5;
int n,m;bool vis[N][N];int id[N][N],tot;
int dx[]={1,1,-1,-1,2,2,-2,-2},dy[]={2,-2,2,-2,1,-1,1,-1};
int S,T,idx=1,head[N*N];
struct edge{int to,next,val;}e[N*N<<5];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;e[++idx]=(edge){from,head[to],0},head[to]=idx;
}
int cur[N*N],dep[N*N],res;
bool bfs(){memset(dep,0,sizeof(dep));queue<int>q;q.push(S),dep[S]=1,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&!dep[to]){dep[to]=dep[p]+1,q.push(to),cur[to]=head[to];if(to==T)return 1;}}}return 0;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(!e[i].val||dep[to]!=dep[p]+1)continue;int c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}return ret;
}
void dinic(){while(bfs())res+=dfs();}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;for(int i=1,x,y;i<=m;i++)cin>>x>>y,vis[x][y]=1;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(!vis[i][j])id[i][j]=++tot;S=0,T=tot+1;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(vis[i][j])continue;if((i+j)&1){for(int k=0;k<8;k++){int x=i+dx[k],y=j+dy[k];if(x<1||x<1||x>n||y>n||vis[x][y])continue;add(id[i][j],id[x][y],inf);}add(S,id[i][j],1);    }else add(id[i][j],T,1);}}dinic();cout<<tot-res<<endl;return 0;
}

火星探险问题

限制性带权路径。

将每个点拆为入点和出点。对于点 \((i,j)\),从其入点往出点连一条流量为 \(\inf\),费用为 \(0\) 的边;若 \((i,j)\) 上有石块,则额外连一条流量为 \(1\),费用为 \(1\) 的边;若 \((i+1,j)\) 可行,则由 \((i,j)\) 的出点向 \((i+1,j)\) 的入点连一条流量为 \(\inf\),费用为 \(0\) 的边;若 \((i,j+1)\) 可行,则由 \((i,j)\) 的出点向 \((i,j+1)\) 的入点连一条流量为 \(\inf\),费用为 \(0\) 的边;由源点向 \((1,1)\) 的入点连一条流量为 \(k\),费用为 \(0\) 的边;由 \((n,n)\) 的出点向汇点连一条流量为 \(k\),费用为 \(0\) 的边。此时的最大费用最大流即为最多收集岩石标本数量。

对于输出方案,先由残量网络求出每个点的经过次数,然后由 \((1,1)\) 进行 \(k\) 次 dfs,每次如果相邻点还能走便继续走。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=105;
const ll inf=1e16+5;
int n,m,k,a[N][N];
int id[N][N][2],tot;
int S,T,idx=1,head[N*N];
struct edge{int to,next;ll val,cst;}e[N*N<<6];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N*N];int cur[N*N];bool vis[N*N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int f[N][N];
void dfs(int x,int y,int i){f[x][y]--;if(x==n&&y==m)return;if(f[x+1][y])return cout<<i<<' '<<0<<'\n',dfs(x+1,y,i);if(f[x][y+1])return cout<<i<<' '<<1<<'\n',dfs(x,y+1,i);
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>k>>m>>n;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)id[i][j][0]=++tot,id[i][j][1]=++tot;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j];S=0,T=tot+1;add(S,id[1][1][0],k,0),add(id[n][m][1],T,k,0);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(a[i][j]==1)continue;if(a[i][j]==2)add(id[i][j][0],id[i][j][1],1,-1);add(id[i][j][0],id[i][j][1],inf,0);if(i<n&&a[i+1][j]!=1)add(id[i][j][1],id[i+1][j][0],inf,0);if(j<m&&a[i][j+1]!=1)add(id[i][j][1],id[i][j+1][0],inf,0);}dinic();for(int x=1;x<=n;x++)for(int y=1;y<=m;y++)for(int i=head[id[x][y][0]];i;i=e[i].next){int to=e[i].to;if(to!=id[x][y][1])continue;if(e[i].cst==-1)f[x][y]+=1-e[i].val;else f[x][y]+=inf-e[i].val;}f[1][1]=f[n][m]=k;for(int i=1;i<=k;i++)dfs(1,1,i);return 0;
}

最长k可重线段集问题

区间选择模型。

容易发现本题与最长k可重区间集问题几乎一致,唯一的区别在于本题中的线段可能与 \(x\) 轴垂直,若两条垂直于 \(x\) 轴的线段 \(x\) 坐标相同,原建图方式会认为两条线段无影响。

考虑在原模型的基础上扩域,对于区间 \((x_1,x_2)\)(或 \([x_1,x_2]\)),将其变为 \((2x_1,2x_2)\)(或 \([2x_1,2x_2]\)).如果 \(x_1 = x_2\) (即区间 \([x_1,x_2]\)),则再将 \(x_2\)\(1\) 变为 \((2x_1,2x_2+1)\) 。由此原先不交的 \((x,x),(x',x')\) 会判定为相交。

但对于原本不交的形如 \((x,x'),(x',x'')\) 的区间,在扩域后会被判定为相交。对于这种情况,我们在原来的基础上,若 \(x_1 \neq x_2\),则将 \(x_1\)\(1\) 变为 \((2x_1+1,2x_2)\) 。可以证明,如此扩域后,可以正确判定线段是否相交。

code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=5005;
const ll inf=1e16+5;
int n,k,l[N],r[N],len[N];
int lsh[N],tot;
int S,T,idx=1,head[N];
struct edge{int to,next;ll val,cst;}e[N<<5];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>k;for(int i=1;i<=n;i++){ll x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;if(x1>x2)swap(x1,x2),swap(y1,y2);len[i]=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));x1*=2,x2*=2;if(x1==x2)l[i]=x1,r[i]=x2+1;else l[i]=x1+1,r[i]=x2;lsh[++tot]=l[i],lsh[++tot]=r[i];}sort(lsh+1,lsh+1+tot);tot=unique(lsh+1,lsh+1+tot)-lsh-1;for(int i=1;i<=n;i++)l[i]=lower_bound(lsh+1,lsh+1+tot,l[i])-lsh,r[i]=lower_bound(lsh+1,lsh+1+tot,r[i])-lsh;S=0,T=tot+1;add(S,1,k,0),add(tot,T,k,0);for(int i=1;i<tot;i++)add(i,i+1,k,0);for(int i=1;i<=n;i++)add(l[i],r[i],1,-len[i]);dinic();cout<<-mncst<<endl;return 0;
}

最长k可重区间集问题

区间选择模型。

先将 \(l,r\) 离散化。对于每个位置 \(i\),向 \(i+1\) 连一条流量为 \(k\),费用为 \(0\) 的边;同时,由源点向点 \(1\),由点 \(n\) 向汇点也连一条流量为 \(k\),费用为 \(0\) 的边。对于区间 \(l_i,r_i\),由点 \(l\) 向点 \(r\) 连一条流量为 \(1\),费用为 \(len_i\)\(len_i\) 为第 \(i\) 条线段离散化前的长度)的边。此时的最大费用最大流即为答案。

对于正确性,每当有一点流流向 \((l_i,r_i)\) 对应的边,相当于选择了对应的区间,由于流量共 \(k\) 点,因此每个位置流量最多流到 \(k\) 个区间上(选择 \(k\) 个区间)。由于是开区间,因此端点处没有影响。

code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=1005;
const ll inf=1e16+5;
int n,k,l[N],r[N];
int lsh[N],tot;
int S,T,idx=1,head[N];
struct edge{int to,next;ll val,cst;}e[N<<5];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>k;for(int i=1;i<=n;i++)cin>>l[i]>>r[i],lsh[++tot]=l[i],lsh[++tot]=r[i];lsh[++tot]=1,lsh[++tot]=1e5;sort(lsh+1,lsh+1+tot);tot=unique(lsh+1,lsh+1+tot)-lsh-1;for(int i=1;i<=n;i++)l[i]=lower_bound(lsh+1,lsh+1+tot,l[i])-lsh,r[i]=lower_bound(lsh+1,lsh+1+tot,r[i])-lsh;S=0,T=tot+1;for(int i=1;i<tot;i++)add(i,i+1,k,0);add(S,1,k,0),add(tot,T,k,0);for(int i=1;i<=n;i++)add(l[i],r[i],1,-lsh[r[i]]+lsh[l[i]]);dinic();cout<<-mncst<<endl;return 0;
}

汽车加油行驶问题

分层图最短路。

对每个点按油量分层,跑分层图最短路。

混进网络流的最短路*2。

code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=105,M=15;
int n,m,a,b,c;bool v[N][N];
int head[N*N*M],idx=1;
struct edge{int to,next,val;}e[N*N*M<<3];
inline void add(int from,int to,int val){e[++idx]=(edge){to,head[from],val},head[from]=idx;}
inline int get(int x,int y,int v){return (x*(n+1)+y)*(m+1)+v;}
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
int dis[N*N*M];bool vis[N*N*M];
struct node{int p,v;friend bool operator<(node x,node y){return x.v>y.v;}
};
void dijkstra(){priority_queue<node>q;memset(dis,0x3f,sizeof(dis));dis[get(1,1,m)]=0,q.push((node){get(1,1,m),0});while(q.size()){int p=q.top().p;q.pop();if(vis[p])continue;vis[p]=1;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(dis[to]>dis[p]+e[i].val){dis[to]=dis[p]+e[i].val;q.push((node){to,dis[to]});}}}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m>>a>>b>>c;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>v[i][j];for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){add(get(i,j,0),get(i,j,m),a+c);for(int k=1;k<=m;k++){add(get(i,j,k),get(i,j,m),a+c);for(int d=0;d<4;d++){int x=i+dx[d],y=j+dy[d];if(x<1||y<1||x>n||y>n)continue;int val=0;if(x<i||y<j)val=b;if(v[x][y])add(get(i,j,k),get(x,y,m),val+a);else add(get(i,j,k),get(x,y,k-1),val);}}}dijkstra();int ans=1e9+5;for(int i=0;i<=m;i++)ans=min(ans,dis[get(n,n,i)]);cout<<ans<<endl;return 0;
}

孤岛营救问题

分层图最短路。

状压钥匙的状态,跑分层图最短路。

混进网络流的最短路*3。

code
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=11;
int n,m,t,k,s;
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
int d[N][N][4],c[N][N];
void build(int x1,int y1,int x2,int y2,int op){for(int i=0;i<4;i++)if(x1+dx[i]==x2&&y1+dy[i]==y2)d[x1][y1][i]|=1<<op;
}
struct node{int x,y,st,v;};
bool vis[N][N][1<<N];
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m>>t>>k;for(int i=1,x1,y1,x2,y2,op;i<=k;i++){cin>>x1>>y1>>x2>>y2>>op;build(x1,y1,x2,y2,op),build(x2,y2,x1,y1,op);}cin>>s;for(int i=1,x,y,op;i<=s;i++)cin>>x>>y>>op,c[x][y]|=1<<op;queue<node>q;q.push((node){1,1,c[1][1],0}),vis[1][1][c[1][1]]=1;while(q.size()){node p=q.front();q.pop();if(p.x==n&&p.y==m)return cout<<p.v<<endl,0;for(int i=0;i<4;i++){if((p.st|d[p.x][p.y][i])!=p.st)continue;int x=p.x+dx[i],y=p.y+dy[i];if(x<1||y<1||x>n||y>m||vis[x][y][p.st])continue;vis[x][y][p.st]=1,q.push((node){x,y,p.st|c[x][y],p.v+1});}}cout<<-1<<endl;return 0;
}

深海机器人问题

限制性带权路径。

对于图中的每条有向道路 \(((i,j),(i',j'),w)\),由 \((i,j)\)\((i',j')\) 连一条流量为 \(1\),费用为 \(x\) ,一条流量为 \(\inf\),费用为 \(0\) 的边;对于可能的出发位置 \(((x,y),k)\),由源点向 \((x,y)\) 连一条流量为 \(k\),费用为 \(0\) 的边; 对于可能的目标位置 \(((x,y),k)\),由 \((x,y)\) 向汇点连一条流量为 \(k\),费用为 \(0\) 的边。此时的最大费用最大流即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=4005;
const ll inf=1e16+5;
int a,b,n,m;
int id[N][N],tot;
int S,T,idx=1,head[N];
struct edge{int to,next;ll val,cst;}e[N<<2];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>a>>b>>n>>m;for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)id[i][j]=++tot;S=0,T=tot+1;for(int i=0;i<=n;i++)for(int j=0,x;j<m;j++)cin>>x,add(id[i][j],id[i][j+1],1,-x),add(id[i][j],id[i][j+1],inf,0);for(int j=0,x;j<=m;j++)for(int i=0;i<n;i++)cin>>x,add(id[i][j],id[i+1][j],1,-x),add(id[i][j],id[i+1][j],inf,0);for(int i=1,k,x,y;i<=a;i++)cin>>k>>x>>y,add(S,id[x][y],k,0);for(int i=1,k,x,y;i<=b;i++)cin>>k>>x>>y,add(id[x][y],T,k,0);dinic();cout<<-mncst<<endl;return 0;
}

数字梯形问题

限制性带权路径。

对于第一问,将每个点拆为入点和出点,对于点 \((i,j)\),由其入点向其出点连流量为 \(1\),费用为 \(a_{i,j}\) 的边;对于 \(1 \leq i \lt n,1 \leq j \leq i + m - 1\),由 \((i,j)\) 的出点向 \((i+1,j)\)\((i+1,j+1)\) 对应的入点连流量为 \(1\),费用为 \(0\) 的边;对于 \(1 \leq j \leq m\),由源点向 \((1,j)\) 对应的入点连流量为 \(1\),费用为 \(0\) 的边;对于 \(1 \leq j \leq n + m - 1\),由 \((n,j)\) 对应的出点向汇点连流量为 \(1\),费用为 \(0\) 的边。此时的最大费用最大流即为答案。

对于第二问,在第一问的基础上,将点 \((i,j)\) 由入点向出点的边与点 \((n,j)\) 向汇点的边流量改为 \(\inf\) 后,跑最大费用最大流即可。

对于第三问,在第二问的基础上,将点 \((i,j)\) 的出点向 \((i+1,j)\)\((i+1,j+1)\) 的入点的边流量改为 \(\inf\) 后,跑最大费用最大流即可。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=55;
const ll inf=1e16+5;
int m,n;ll a[N][N];
int id[N][N][2],tot;
int S,T,idx=1,head[N*N];
struct edge{int to,next;ll val,cst;}e[N*N<<5];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N*N];int cur[N*N];bool vis[N*N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>m>>n;for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)cin>>a[i][j],id[i][j][0]=++tot,id[i][j][1]=++tot;S=0,T=tot+1;for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][0],id[i][j][1],1,-a[i][j]);for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][1],id[i+1][j][0],1,0),add(id[i][j][1],id[i+1][j+1][0],1,0);for(int i=1;i<=m;i++)add(S,id[1][i][0],1,0);for(int i=1;i<=n+m-1;i++)add(id[n][i][1],T,1,0);dinic();cout<<-mncst<<endl;memset(head,0,sizeof(head));idx=1;for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][0],id[i][j][1],inf,-a[i][j]);for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][1],id[i+1][j][0],1,0),add(id[i][j][1],id[i+1][j+1][0],1,0);for(int i=1;i<=m;i++)add(S,id[1][i][0],1,0);for(int i=1;i<=n+m-1;i++)add(id[n][i][1],T,inf,0);dinic();cout<<-mncst<<endl;memset(head,0,sizeof(head));idx=1;for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][0],id[i][j][1],inf,-a[i][j]);for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++)add(id[i][j][1],id[i+1][j][0],inf,0),add(id[i][j][1],id[i+1][j+1][0],inf,0);for(int i=1;i<=m;i++)add(S,id[1][i][0],1,0);for(int i=1;i<=n+m-1;i++)add(id[n][i][1],T,inf,0);dinic();cout<<-mncst<<endl;return 0;
}

分配问题

最小/大费用最大流。

由源点向每个人连流量为 \(1\),费用为 \(0\) 的边,每件工作向汇点连流量为 \(1\),费用为 \(0\) 的边,第 \(i\) 个人向第 \(j\) 个工作连流量为 \(1\),费用为 \(c_{i,j}\) 的边,此时的最小/大费用最大流即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=55,inf=1e9+5;
int n,a[N][N];
int S,T,idx=1,head[N*2];
struct edge{int to,next,val,cst;}e[N*N<<2];
inline void add(int from,int to,int val,int cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
int dis[N*2],cur[N*2];bool vis[N*2];
int mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])vis[to]=1,q.push(to);}}}return dis[T]<inf;
}
int dfs(int p=S,int flow=inf){if(p==T)return flow;int ret=0;vis[p]=1;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;int c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=0,mncst=0;while(spfa()){int res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>a[i][j];S=0,T=2*n+1;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)add(i,j+n,1,a[i][j]);for(int i=1;i<=n;i++)add(S,i,1,0);for(int i=1;i<=n;i++)add(i+n,T,1,0);dinic();cout<<mncst<<endl;idx=1;for(int i=S;i<=T;i++)head[i]=0;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)add(i,j+n,1,-a[i][j]);for(int i=1;i<=n;i++)add(S,i,1,0);for(int i=1;i<=n;i++)add(i+n,T,1,0);dinic();cout<<-mncst<<endl;return 0;
}

运输问题

最小费用最大流。

由源点向第 \(i\) 个商店连流量为 \(a_i\) ,费用为 \(0\) 的边,第 \(j\) 个仓库向汇点连流量为 \(b_j\),费用为 \(0\) 的边,第 \(i\) 个商店向第 \(j\) 个仓库连流量为 \(\inf\) ,费用为 \(c_{i,j}\) 的边。此时的最小/大费用最大流即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=105;
const ll inf=1e16+5;
int n,m,a[N],b[N],c[N][N];
int S,T,idx=1,head[N*2];
struct edge{int to,next;ll val,cst;}e[N*N<<1];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N*2];bool vis[N*2];int cur[N*2];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])vis[to]=1,q.push(to);}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(flow,e[i].val));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n>>m;S=0,T=n+m+1;for(int i=1;i<=n;i++)cin>>a[i],add(S,i,a[i],0);for(int i=1;i<=m;i++)cin>>b[i],add(i+n,T,b[i],0);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>c[i][j],add(i,j+n,inf,c[i][j]);dinic();cout<<mncst<<endl;idx=1;for(int i=0;i<=n+m+1;i++)head[i]=0;for(int i=1;i<=n;i++)add(S,i,a[i],0);for(int i=1;i<=m;i++)add(i+n,T,b[i],0);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)add(i,j+n,inf,-c[i][j]);dinic();cout<<-mncst<<endl;return 0;
}

负载平衡问题

最小费用最大流。

\(m = \dfrac{\sum_{1 \leq i \leq n}a_i}{n}\),对于每个 \(i\),若 \(a_i \gt m\),则由源点向点 \(i\) 连一条流量为 \(a_i - m\),费用为 \(0\) 的边;若 \(a_i \lt m\),则由点 \(i\) 向汇点连一条流量为 \(m - a_i\),费用为 \(0\) 的边。同时,由 \(i\)\(i-1,i+1\) 连一条流量为 \(\inf\),费用为 \(1\) 的边。此时的最小费用最大流即为答案。

code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
using ll=long long;
const int N=205,M=20005;
const ll inf=1e16+5;
int n;ll m,a[N];
int S,T,idx=1,head[N];
struct edge{int to,next;ll val,cst;}e[M<<1];
inline void add(int from,int to,ll val,ll cost){e[++idx]=(edge){to,head[from],val,cost},head[from]=idx;e[++idx]=(edge){from,head[to],0,-cost},head[to]=idx;
}
ll dis[N];int cur[N];bool vis[N];
ll mxflow,mncst;
bool spfa(){memset(dis,0x3f,sizeof(dis));queue<int>q;q.push(S),dis[S]=0,cur[S]=head[S];while(q.size()){int p=q.front();q.pop();vis[p]=0;for(int i=head[p];i;i=e[i].next){int to=e[i].to;if(e[i].val&&dis[to]>dis[p]+e[i].cst){dis[to]=dis[p]+e[i].cst,cur[to]=head[to];if(!vis[to])q.push(to),vis[to]=1;}}}return dis[T]<inf;
}
ll dfs(int p=S,ll flow=inf){if(p==T)return flow;vis[p]=1;ll ret=0;for(int i=cur[p];i&&flow;i=e[i].next){int to=e[i].to;cur[p]=i;if(vis[to]||!e[i].val||dis[to]!=dis[p]+e[i].cst)continue;ll c=dfs(to,min(e[i].val,flow));e[i].val-=c,e[i^1].val+=c;flow-=c,ret+=c;}vis[p]=0;return ret;
}
void dinic(){mxflow=mncst=0;while(spfa()){ll res=dfs();mxflow+=res,mncst+=res*dis[T];}
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cin>>n;for(int i=1;i<=n;i++)cin>>a[i],m+=a[i];m/=n;S=0,T=n+1;for(int i=1;i<=n;i++)if(a[i]>m)add(S,i,a[i]-m,0);else add(i,T,m-a[i],0);for(int i=1;i<=n;i++){add(i,i%n+1,inf,1);add(i%n+1,i,inf,1);}dinic();cout<<mncst<<endl;return 0;
}








机器人路径规划问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/865513.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LAS点云如何转换为3DTiles格式?用这款免费工具轻松搞定!

概述 随着数字孪生和三维城市模型技术的迅速发展,点云数据的使用越来越广泛。而LAS格式作为常见的点云数据存储格式,因其精确度高、数据丰富,成为了地形测绘、建筑建模等领域的主流数据源。然而,如何将这些庞大的LAS点云数据转换成适用于Web端展示的3DTiles格式,成了许多开…

linux系统串口终端软件显示异常解决方法,触觉智能出品

分享linux系统串口终端软件显示异常解决方法,以xshell软件为例本文介绍linux系统串口终端软件显示异常解决方法,以xshell软件为例。使用触觉智能的Purple Pi R1双网口开发板演示,内置双核A7 1.2Ghz处理器,支持WiFi与丰富GPIO拓展,是嵌入式新人入门学习的高性价比开发板! …

SecureFX for Mac FTP/SSH传输工具

SecureFX for Mac FTP/SSH传输工具 SecureFX mac破解版是一款Mac平台的FTP/SSH传输工具。SecureFX for Mac支持三种文件传输协议:FTP、SFTP 和 FTP over SSH2。它可以提供安全文件传输。无论您连接的是任何一种操作系统的服务器,它都能提供安全的传输服务。它主要用于Linux操…

Debian 11/12一键更换国内系统源

有时候买了国内的服务器,更新系统或者安装软件速度相当慢,这时候我们可以将系统源一键更换为国内的源,速度就会快很多!一、备份现有apt源配置 cp -a /etc/apt/sources.list /etc/apt/sources.list.bak二、替换为其他apt源 Debian11 1、清华源 这是一条命令,全部复制后,直…

创建销售订单BAPI报错,SLS_LORD 033

只提示一个凭证 不能保存的消息,没有任何提示及字段信息 跟消息断点发现是被增强卡住了

用 Modbus 软件配置 Modbus tcp 转 ETHERCAT 步骤

在工业自动化控制系统中,常常会遇到不同协议设备集成的需求。例如,部分设备采用 Modbus tcp 协议进行通信,而另一些设备则基于 ETHERCAT 协议运行。这种协议差异导致设备之间无法直接进行数据交互,严重影响了整个系统的协同工作和自动化程度。为解决这一问题,我们引入捷米…

pd虚拟机 [po] Parallels Desktop 20 激活 for Mac [jie] 安装教程【支持M芯片】

pd虚拟机 [po] Parallels Desktop 20 激活 for Mac [jie] 安装教程【支持M芯片】 Parallels Desktop 20 ,是一款Mac虚拟机软件,在搭载Apple M 系列芯片的任何 Mac 上运行 Windows,体验不同操作系统之间无缝集成。使用 Parallels Desktop 20 for Mac 体验 macOS 和 Windows 的…

【触想智能】什么是嵌入式工业一体机以及它的特点和应用领域分析

嵌入式工业一体机是一种集成了计算、控制、通信等功能的高度集成化设备。它在工业自动化领域中有着广泛的应用,为工厂生产、数据处理、监控和管理提供了全面的解决方案。下面是嵌入式工业一体机的定义、特点以及主要应用领域的具体介绍。一、嵌入式工业一体机的定义嵌入式工业…

VMware Fusion Pro 13 for Mac虚拟机软件

VMware Fusion Pro 13 for Mac虚拟机软件 VMware Fusion Pro for Mac,是一款mac虚拟机软件,跟Parallels Desktop一样,都可以让你的 Mac 同时运行一个或多个不同的操作系统。VMware Fusion Pro mac不仅能让你在Mac苹果电脑上运行Windows或Linux系统、使用非Mac平台的软件,而…

空压机网络接入实战:基于 MODBUS - TCP 转 Ethernet IP 网关的配置过程剖析

在工业自动化生产环境中,空压机作为重要的气源设备,其稳定运行和有效监控对于整个生产流程至关重要。然而,不同厂家生产的空压机可能采用不同的通信协议,这给集中监控和管理带来了挑战。在本次案例中,我们遇到的空压机采用 MODBUS - TCP 协议进行数据传输,但企业的自动化…

【CUMT】软件工程期末复习

1.软件工程概述1.2 计算机软件概述 1.4 软件生命周期 1.5 软件开发模型2.项目计划2.2 可行性研究3.需求分析3.2 数据流图DFD 3.3 数据字典DD 3.4 加工逻辑描述工具4.面向对象方法(OOA)4.2 统一建模语言UML 4.3 用例模型5.概要设计5.1 软件开发流程概念区分 5.2 软件结构设计 …

【vulnhub靶场】日志投毒与文件包含漏洞 Solstice

一、侦查1.1 收集目标网络信息:IP地址1.2 主动扫描:扫描IP地址段 二、初始访问2.1 利用面向公众的应用 三、权限提升3.1 利用漏洞提权:高权限运行的程序一、侦查 1.1 收集目标网络信息:IP地址 靶机启动后,没有提供IP地址。由于Kali和靶机在同一个C段,可以扫描ARP协议获取…