【学习笔记】网络流
# 〇:【前言】
2026.1.29竣工
# 壹:【板子】
网络流有五个常见优化:分层图,多路增广,当前弧优化,空流优化,炸点优化,
分层图和多路增广组成了Dinic
当前弧优化是保证Dinic时间复杂度正确的最重要的优化
而空流优化和炸点优化,优化得并不是很明显
P3376 【模板】网络最大流
#include<bits/stdc++.h>
#define inf 1e18
using namespace std;int n,m,s,t;
typedef long long LL;
const int N=210,M=1e4+10;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;
}
int dep[N],now[N];
bool bfs(){//分层图
for(int i=1;i<=n;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(s);dep[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
LL dfs(int u,LL las){//多路增广
if(u==t) return las;
LL ans=0;
for(int i=now[u];i&&las;i=ne[i]){//空流优化
int v=to[i];
now[u]=i;//当前弧优化
if(w[i]&&dep[u]+1==dep[v]){
int res=dfs(v,min(las,(LL)w[i]));
ans+=res;
las-=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;//炸点优化
return ans;
}
LL Dinic(){
LL ans=0;
while(bfs()) ans+=dfs(s,inf);
return ans;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>s>>t;
while(m--){
int a,b,c;cin>>a>>b>>c;
add(a,b,c);
add(b,a,0);
}
cout<<Dinic()<<"\n";
return 0;
}
P3381 【模板】最小费用最大流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=5010,M=1e5+10;int n,m,s,t;
int h[N],to[M],w[M],c[M],ne[M],idx=1;
int dis[N],vis[N];
int now[N];
void add(int u,int v,int ww,int cc){
to[++idx]=v;w[idx]=ww;ne[idx]=h[u];h[u]=idx;c[idx]=cc;
}
bool bfs(){
for(int i=1;i<=n;i++){
now[i]=h[i];
dis[i]=inf;
}
queue<int> q;q.push(s);dis[s]=0;vis[s]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+c[i]){
dis[v]=dis[u]+c[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[t]!=inf;
}
int cosn=0;
int dfs(int u,int las){
if(u==t) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(dis[v]==dis[u]+c[i]&&w[i]&&!vis[v]){
int res=dfs(v,min(las,w[i]));
ans+=res;
las-=res;
w[i]-=res;
w[i^1]+=res;
cosn+=res*c[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(s,inf);
return ans;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m>>s>>t;
while(m--){
int u,v,w,c;cin>>u>>v>>w>>c;
add(u,v,w,c);
add(v,u,0,-c);
}
cout<<Dinic()<<" ";
cout<<cosn<<"\n";
return 0;
}
P14578 【模板】无源汇上下界可行流
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1010,M=(1e4+2*N)*2;int n,m,s,t;
int h[N],ne[M],w[M],to[M],idx=1;
int beg[M];
int in[N];
void add(int a,int b,int c){
to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;
}
int Graph(){
for(int i=1;i<=m;i++){
int a,b,l,r;cin>>a>>b>>l>>r;
r-=l;
add(a,b,r);
add(b,a,0);
in[b]+=l;in[a]-=l;
beg[i]=l;
}
s=n+1;t=n+2;
int ans=0;
for(int i=1;i<=n;i++){
if(in[i]>0){
add(s,i,in[i]);
add(i,s,0);
ans+=in[i];
}
else{
add(i,t,-in[i]);
add(t,i,0);
}
}
return ans;
}
int dep[N],now[N];
bool bfs(){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(s);dep[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
int dfs(int u,int las){
if(u==t) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int res=dfs(v,min(las,w[i]));
las-=res;
ans+=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(s,inf);
return ans;
}
int main(){
cin>>n>>m;
int num=Graph();
int ans=Dinic();
if(num!=ans) cout<<"No\n";
else{
cout<<"Yes\n";
for(int i=1;i<=m;i++) cout<<beg[i]+w[i*2+1]<<"\n";
}
return 0;
}
P14579 【模板】有源汇上下界最大流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=1010,M=(1e4+N*2)*2;int n,m,s,t,ss,tt;
int in[N];
int h[N],to[M],w[M],ne[M],idx=1;
int add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
return idx;
}
int id;
int Graph(){
for(int i=1;i<=m;i++){
int a,b,l,r;cin>>a>>b>>l>>r;
r-=l;
add(a,b,r);
add(b,a,0);
in[b]+=l;in[a]-=l;
}
add(t,s,inf);
id=add(s,t,0);
ss=n+1,tt=n+2;
int ans=0;
for(int i=1;i<=n;i++){
if(in[i]>0){
ans+=in[i];
add(ss,i,in[i]);
add(i,ss,0);
}
else{
add(i,tt,-in[i]);
add(tt,i,0);
}
}
return ans;
}
int dep[N],now[N];
bool bfs(int s,int t){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(s);dep[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
int dfs(int u,int las,int t){
if(u==t) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int res=dfs(v,min(las,w[i]),t);
las-=res;
ans+=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(int s,int t){
int ans=0;
while(bfs(s,t)) ans+=dfs(s,inf,t);
return ans;
}
int main(){
cin>>n>>m>>s>>t;
int num=Graph();
int ans=Dinic(ss,tt);
if(num!=ans) cout<<"N\n";
else{
int ans=w[id];
w[id]=w[id^1]=0;
cout<<ans+Dinic(s,t)<<"\n";
}
return 0;
}
P14580 【模板】有源汇上下界最小流
#include<bits/stdc++.h>
#define inf 1e9+10
using namespace std;
const int N=1010,M=(1e4+N*2)*2;int n,m,s,t,ss,tt;
int in[N];
int h[N],to[M],w[M],ne[M],idx=1;
int add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
return idx;
}
int id;
int Graph(){
for(int i=1;i<=m;i++){
int a,b,l,r;cin>>a>>b>>l>>r;
r-=l;
add(a,b,r);
add(b,a,0);
in[b]+=l;in[a]-=l;
}
add(t,s,inf);
id=add(s,t,0);
ss=n+1,tt=n+2;
int ans=0;
for(int i=1;i<=n;i++){
if(in[i]>0){
ans+=in[i];
add(ss,i,in[i]);
add(i,ss,0);
}
else{
add(i,tt,-in[i]);
add(tt,i,0);
}
}
return ans;
}
int dep[N],now[N];
bool bfs(int s,int t){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(s);dep[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
int dfs(int u,int las,int t){
if(u==t) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int res=dfs(v,min(las,w[i]),t);
las-=res;
ans+=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(int s,int t){
int ans=0;
while(bfs(s,t)) ans+=dfs(s,inf,t);
return ans;
}
int main(){
cin>>n>>m>>s>>t;
int num=Graph();
int ans=Dinic(ss,tt);
if(num!=ans) cout<<"N\n";
else{
int ans=w[id];//cout<<ans<<"\n";
w[id]=w[id^1]=0;
cout<<ans-Dinic(t,s)<<"\n";
}
return 0;
}
# 贰:【二十四题选做】
题单
以下内容经过总结,分为图上状态转移、DAG最小路径覆盖、二分图、不相交路径等问题
一:【P2754 [CTSC1999] 家园 / 星际转移问题】
1.【题面】
2.【解法】
按照时间分层,跑最大流
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e5+10,M=100,S=0,T=N-1;int n;
vector<int> g[M];int num[M],up[M];
int bcj[M];
int find(int x){
if(x==bcj[x]) return x;
return bcj[x]=find(bcj[x]);
}
void merge(int a,int b){
a=find(a);b=find(b);
if(a==b) return ;
bcj[a]=b;
}
int h[N],to[N],w[N],ne[N],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=0;i<N;i++) dep[i]=-1;
for(int i=0;i<N;i++) now[i]=h[i];
queue<int> q;
q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[u]+1==dep[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
ans+=k;
las-=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
int fc(int t,int x){
return t*n+x;
}
int main(){
int m,K;cin>>n>>m>>K;
for(int i=0;i<M;i++) bcj[i]=i;
for(int i=1;i<=m;i++){
cin>>up[i];
int r;cin>>r;
num[i]=r;
while(r--){
int k;cin>>k;
if(k==0) k=n+1;
if(k==-1) k=n+2;
if(g[i].size()) merge(g[i].back(),k);
g[i].push_back(k);
}
}
n+=2;
if(find(n-1)!=find(n)){
cout<<0<<"\n";
return 0;
}
add(S,fc(0,n-1),inf);add(fc(0,n),T,inf);
int Time=0;
int ans=0;
while(1){
for(int i=fc(Time,1);i<=fc(Time,n);i++) add(i,i+n,inf);
for(int i=1;i<=m;i++){
int a=g[i][Time%num[i]];
int b=g[i][(Time+1)%num[i]];
add(fc(Time,a),fc(Time+1,b),up[i]);
}
add(S,fc(Time+1,n-1),inf);add(fc(Time+1,n),T,inf);
Time++;
ans+=Dinic();
if(ans>=K){
cout<<Time<<"\n";
return 0;
}
}
return 0;
}
二:【P2762 太空飞行计划问题】
1.【题面】
2.【解法】
(1)【转化】
选实验,必须选对应仪器;不选实验,可以不选对应仪器
转化得到,不放弃实验,必须选对应仪器;放弃实验,可以不选对应仪器
继续转化,当实验取 \(0\) (不放弃),所有对应的仪器都要取 \(1\) ;实验取 \(1\) (放弃),对应的仪器取值任意
得到总消耗 \(res\)
用所有实验的总收入 \(all\) 减去 \(res\) 得到 \(ans\) 就是一组解
使 \(ans\) 最大,\(res\) 需要最小,最小割模型
(2)【建图】
\(s\) 向每个实验连容量为 \(p\) 的边,每个实验向对应仪器连容量 \(inf\) 的边,每个仪器向 \(t\) 连容量为 \(c\) 的边
(3)【补充】
把题目抽象出来,得到
给定一张图,有左侧的点和右侧的点,左侧的点点权为正(对应试验),右侧的点点权为负(对应器材),如果选择了左侧的某个点就必须要选右边的一部分点。要求最大化点权和。
如果将左侧的点和右侧的点之间对应连边,如果该实验要求该器材就连一条边,那么问题就被转化为了这样一个问题:
给定一个有向图,点有点权,选择一个子图,满足子图上如果选择了一个点就必须选择它后继的所有点。最大化点权和。
这一类问题被称为最大权闭合子图问题,都可以用上述最小割解法解决
3.【代码】
#include<bits/stdc++.h>
#define int long long
#define inf 1e18
using namespace std;
const int N=210,M=5010,S=0,T=N-1;
int cap[N];
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[u]+1==dep[v]){
int res=dfs(v,min(las,w[i]));
ans+=res;
las-=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
signed main(){
int n,m;cin>>m>>n;
int sum=0;
for(int i=1;i<=m;i++){
cin>>cap[i];
sum+=cap[i];
add(S,i,cap[i]);
char tools[10000];
memset(tools,0,sizeof tools);
cin.getline(tools,10000);
int ulen=0,tool;
while(sscanf(tools+ulen,"%d",&tool)==1){
add(i,tool+m,inf);
if(tool==0)
ulen++;
else{
while(tool){
tool/=10;
ulen++;
}
}
ulen++;
}
}
for(int i=1;i<=n;i++){
int k;cin>>k;
add(i+m,T,k);
}
int ans=Dinic();
cout<<sum<<" "<<ans<<"\n";
for(int i=1;i<=m;i++){
if(dep[i]!=-1) cout<<i<<" ";
}cout<<"\n";
for(int i=1;i<=n;i++){
if(dep[i+m]!=-1) cout<<i<<" ";
}cout<<"\n";
ans=sum-ans;
cout<<ans<<"\n";
return 0;
}
4.【补充】
跑完最大流后,\(S\) 和 \(T\) 一定被割成两部分,由于最大流=最小割,所以最小割一定在满流的边构成的集合中
所以存在一组最小割满足 \(dep!=-1\) 的是 \(S\) 部分,否则是 \(T\) 部分
三:【P2764 最小路径覆盖问题】
1.【题意】
2.【解法】
我们先假设有 \(n\) 个路径,每个路径一个点,然后合并路径
我们把每个点 \(u\) 拆成 \(x\) , \(y\) 两部分,\(s\) 向 \(x_u\) 连一条 \(1\) 容量的边,\(y_u\) 向 \(t\) 连一条 \(1\) 容量的边
如果 \(a\) 和 \(b\) 连边,则 \(x_a-1->y_b\)
然后跑网络流,得到最大合并次数
用 \(n\) 减去即可
3.【证明】
\(x\) 是接入部分,如果 \(x_a->y_b\) 的边流满,则 \(a\) 的后继是 \(b\)
\(y\) 是被接入部分,如果 \(x_a->y_b\) 的边流满,则 \(b\) 的前驱是 \(a\)
一个点,最多有一个后继,一个前驱,所有边容量都为 \(1\)
跑最大流可以得到最大匹配数即合并次数
4.【代码】
#include<bits/stdc++.h>
#define X(a) (a)
#define Y(b) (b+n)
#define inf 1e9
using namespace std;
const int N=510,M=2e5+10,S=0,T=N-1;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[u]+1==dep[v]){
int res=dfs(v,min(las,w[i]));
ans+=res;
las-=res;
w[i]-=res;
w[i^1]+=res;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
int las[N];
struct Edg{
int a,b;
}edg[M];
int ind[N];
void dfs(int x){
if(las[x]) dfs(las[x]);
cout<<x<<" ";
}
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
add(X(a),Y(b),1);
edg[i]={a,b};
}
for(int i=1;i<=n;i++){
add(S,X(i),1);
add(Y(i),T,1);
}
int ans=n-Dinic();
for(int i=1;i<=m;i++){
if(w[i*2]==0){
las[edg[i].b]=edg[i].a;
ind[edg[i].a]++;
}
}
for(int i=1;i<=n;i++){
if(ind[i]==0){
dfs(i);
cout<<"\n";
}
}
cout<<ans<<"\n";
return 0;
}
四:【P2765 魔术球问题】
1.【题意】
2.【解法】
动态加点【P2764 最小路径覆盖问题】
3.【代码】
#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+N/2)
#define inf 1e9
using namespace std;
const int N=3e5+10,M=5*N,S=0,T=N-1;
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=0;i<N;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int flow;
while(flow=dfs(S,inf)) ans+=flow;
}
return ans;
}
int vis[N];
int nxt[N];
struct Edg{
int a,b;
}edg[M];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n;cin>>n;
int res=0,num=0;
int idx=0;
while(num-res<=n){
num++;
for(int i=sqrt(num);i*i<N;i++){
int b=i*i-num;
if(b<=0) continue;
if(b>=num) break;
add(X(b),Y(num),1);
edg[++idx]={b,num};
}
add(S,X(num),1);
edg[++idx]={-1,-1};
add(Y(num),T,1);
edg[++idx]={-1,-1};
res+=Dinic();
}
cout<<num-1<<"\n";
for(int i=1;i<=idx;i++){
if(edg[i].a==-1) continue;
int a=edg[i].a,b=edg[i].b;
if(w[i*2]==0){
nxt[a]=b;
}
}
for(int i=1;i<=num-1;i++){
if(vis[i]) continue;
int x=i;
while(x&&!vis[x]){
vis[x]=1;
cout<<x<<" ";
x=nxt[x];
}cout<<"\n";
}
return 0;
}
五:【P2763 试题库问题】
1.【题意】
2.【解法】
\(s\) 向每个试题连容量为 \(1\) 的边,每个类型向 \(t\) 连容量为所包含题数的边,试题向所属类型连容量为 \(1\) 的边
跑最大流
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e4+10;
int S=0,T;
int h[N],to[N],w[N],ne[N],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
ans+=k;
las-=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
void print(int u){
for(int i=h[u];i;i=ne[i]){
int v=to[i];
//cout<<u<<" "<<v<<" "<<w[i]<<" "<<i<<"\n";
if(w[i]) cout<<v<<" ";
}
}
int main(){
int K,n;cin>>K>>n;
T=n+K+1;
int m=0;
for(int i=1;i<=K;i++){
int k;cin>>k;
m+=k;
add(n+i,T,k);
}
for(int i=1;i<=n;i++){
add(S,i,1);
int r;cin>>r;
while(r--){
int k;cin>>k;
add(i,k+n,1);
}
}
int ans=Dinic();
if(ans!=m){
cout<<"No Solution!\n";
return 0;
}
for(int i=1;i<=K;i++){
cout<<i<<": ";
print(n+i);
cout<<"\n";
}
return 0;
}
六:【P4014 分配问题】
1.【题意】
2.【解法】
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个工作连权值为 \((1,0)\) 的边,每个工作 \(a\) 向所有人 \(b\) 连权值为 \((1,c)\) 的边,每个人向 \(t\) 连权值为 \((1,0)\) 的边
跑费用流
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=210,M=N*N;
int S=0,T;
int h[N],to[M],w[M],cost[M],ne[M],idx=1;
void add(int a,int b,int c,int d){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int mincost;
int dis[N],now[N],vis[N];
bool SPFAmin(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
vis[i]=0;
}
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[T]!=inf;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
ans+=k;
las-=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(SPFAmin()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
int n;cin>>n;
T=n+n+1;
for(int i=1;i<=n;i++){
add(S,i,1,0);
add(i+n,T,1,0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int c;cin>>c;
add(i,j+n,1,c);
}
}
Dinic();
cout<<mincost<<"\n";
for(int i=2;i<=idx;i+=2){
w[i]+=w[i^1];w[i^1]=0;
cost[i]=-cost[i];
cost[i^1]=-cost[i^1];
}
mincost=0;
Dinic();
cout<<-mincost<<"\n";
return 0;
}
七:【P2774 方格取数问题】
1.【题意】
2.【解法】
按照横坐标+纵坐标奇偶分点,奇数为白,偶数为黑
\(s\) 向白点连容量为点权的边,黑点向 \(t\) 连容量为点权的边
白点向相邻黑点连容量 \(inf\) 的边
跑最小割,用总权值减去
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1e4+10,M=4*N;int n,m;
int S=0,T;
int h[N],w[M],to[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int cl(int x,int y){
return (x-1)*m+y;
}
int dep[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
cin>>n>>m;
T=n*m+1;
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int k;cin>>k;
ans+=k;
if((i+j)&1) add(S,cl(i,j),k);
else add(cl(i,j),T,k);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)%2==0) continue;
if(i>1) add(cl(i,j),cl(i-1,j),inf);
if(i<n) add(cl(i,j),cl(i+1,j),inf);
if(j>1) add(cl(i,j),cl(i,j-1),inf);
if(j<m) add(cl(i,j),cl(i,j+1),inf);
}
}
ans-=Dinic();
cout<<ans<<"\n";
return 0;
}
八:【P3355 骑士共存问题】
1.【题意】
2.【解法】
同【P2774 方格取数问题】
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=4e4+10,M=10*N;int n;
int S=0,T;
int h[N],w[M],to[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int cl(int x,int y){
return (x-1)*n+y;
}
int dep[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int vis[N];
int fx[8][2]={{-2,-1},{-1,-2},{2,-1},{-1,2},{-2,1},{1,-2},{2,1},{1,2}};
#define inmap(x,y) (1<=x&&x<=n&&1<=y&&y<=n)
int main(){
int m;cin>>n>>m;
T=n*n+1;
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
vis[cl(a,b)]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(vis[cl(i,j)]) continue;
if((i+j)&1) add(S,cl(i,j),1);
else add(cl(i,j),T,1);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(vis[cl(i,j)]) continue;
if((i+j)%2==0) continue;
for(int k=0;k<8;k++){
int x=i+fx[k][0],y=j+fx[k][1];
if(!inmap(x,y)) continue;
if(vis[cl(x,y)]) continue;
add(cl(i,j),cl(x,y),inf);
}
}
}
cout<<n*n-m-Dinic()<<"\n";
return 0;
}
九:【P4016 负载平衡问题】
1.【题意】
2.【解法】
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个仓库连一条 \((a,0)\) 的边,相邻两个仓库之间连一条 \((inf,1)\) 的边,每个仓库向 \(t\) 连一条 \((平均值,0)\) 的边
跑最小费用最大流
3.【代码】
费用流
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=310,M=1e5+10;
int S,T;
int h[N],to[M],ne[M],w[M],idx=1;
int cost[M];
void add(int a,int b,int c,int d){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
}
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(SPFA()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
int n;cin>>n;
T=n+1;
int sum=0;
for(int i=1;i<=n;i++){
int k;cin>>k;
sum+=k;
add(S,i,k,0);
}
for(int i=1;i<=n;i++){
int a=i-1;
if(a==0) a=n;
int b=i+1;
if(b==n+1) b=1;
add(i,a,inf,1);
add(i,b,inf,1);
}
sum/=n;
for(int i=1;i<=n;i++){
add(i,T,sum,0);
}
Dinic();
cout<<mincost<<"\n";
return 0;
}
贪心
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int mp[N],qh[N];
int main(){
int n;cin>>n;
int X=0;
for(int i=1;i<=n;i++){
cin>>mp[i];
X+=mp[i];
}
X/=n;
for(int i=1;i<=n;i++) qh[i]=qh[i-1]+mp[i]-X;
int ans=0;
sort(qh+1,qh+1+n);
int C=qh[n/2+1];
for(int i=1;i<=n;i++) ans+=abs(C-qh[i]);
cout<<ans<<endl;
return 0;
}
十:【P4015 运输问题】
1.【题意】
2.【解法】
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
\(s\) 向每个仓库连 \((a,0)\) 的边,每个仓库 \(x\) 向所有商店 \(y\) 连 \((inf,c_{x,y})\) 的边,每个商店向 \(t\) 连 \((b,0)\) 的边
跑最小费用最大流
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=510,M=1e5+10;
int S,T;
int h[N],to[M],ne[M],w[M],idx=1;
int cost[M];
void add(int a,int b,int c,int d){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
}
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(SPFA()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
int m,n;cin>>m>>n;
T=n+m+1;
for(int i=1;i<=m;i++){
int k;cin>>k;
add(S,i,k,0);
}
for(int i=1;i<=n;i++){
int k;cin>>k;
add(i+m,T,k,0);
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
int k;cin>>k;
add(i,j+m,inf,k);
}
}
Dinic();
cout<<mincost<<"\n";
mincost=0;
for(int i=2;i<=idx;i+=2){
w[i]+=w[i^1];
w[i^1]=0;
cost[i]=-cost[i];
cost[i^1]=-cost[i^1];
}
Dinic();
cout<<-mincost<<"\n";
return 0;
}
十一:【P2766 最长不下降子序列问题】
1.【题意】
2.【解法】
(1)【第一问】
跑 \(dp\) 求解,\(f_i\) 表示以 \(i\) 为末尾最长不下降子序列长度
记最长不下降子序列长度为 \(K\)
(2)【第二问】
把每个点拆成 \(x,y\) 两部分, \(x\) 表被其他点接入, \(y\) 表接入其他点
\(s\) 向每个 \(x_i\) (满足 \(f_i==1\) )连一条容量为 \(1\) 的边,每个 \(x_i\) 向 \(y_i\) 连一条容量为 \(1\) 的边,每个 \(y_i\) (满足 \(f_i==K\) )向 \(t\) 连一条容量为 \(1\) 的边
对于二元组 \((i,j)\) 满足 \(num_i \le num_j且f_i+1==f_j\) ,则 \(y_i -1-> x_j\)
跑最大流
(3)【第三问】
如果存在
则把边 \((s,x_1),(x_1,y_1),(x_n,y_n),(y_n,t)\) 的容量改为 \(inf\)
跑最大流
3.【代码】
#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+n)
#define inf 1e9
using namespace std;
const int N=2010,M=1e5+10;
int S,T;
int a[N],dp[N];
int h[N],to[M],w[M],ne[M],idx=1;
void add(int a,int b,int c){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;
}
int dep[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dep[i]=-1;
now[i]=h[i];
}
queue<int> q;q.push(S);dep[S]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dep[v]==-1){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dep[v]==dep[u]+1){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
}
}
if(ans==0) dep[u]=-1;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
int n;cin>>n;
T=n+n+1;
for(int i=1;i<=n;i++) cin>>a[i];
int K=0;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(a[j]<=a[i]) dp[i]=max(dp[i],dp[j]);
}
dp[i]+=1;
K=max(dp[i],K);
}
cout<<K<<"\n";
int cg1=-1,cg2=-1,cg3=-1,cg4=-1;
for(int i=1;i<=n;i++){
add(X(i),Y(i),1);
if(i==1) cg2=idx-1;
if(i==n) cg3=idx-1;
if(dp[i]==1){
add(S,X(i),1);
if(i==1) cg1=idx-1;
}
if(dp[i]==K){
add(Y(i),T,1);
if(i==n) cg4=idx-1;
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(a[i]<=a[j]&&dp[i]+1==dp[j]) add(Y(i),X(j),1);
}
}
cout<<Dinic()<<"\n";
for(int i=2;i<=idx;i+=2){
w[i]+=w[i^1];w[i^1]=0;
}
w[cg1]=w[cg2]=w[cg3]=w[cg4]=inf;
if(n==1) cout<<1<<"\n";
else cout<<Dinic()<<"\n";
return 0;
}
十二:【P2770 航空路线问题】
1.【题意】
使只在 \(s\) 和 \(t\) 交的两条路径长度和最大
2.【解法】
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
把每个点拆成入点 \(x\) 和出点 \(y\) ,对于每个 \(i(2 \le i \le n-1)\) ,入点向出点连 \((1,1)\) 的边,对于 \(1\) 和 \(n\) ,入点向出点连 \((2,1)\) 的边
如果 \(a\) 和 \(b\) 连边,则 \(y_a\) 向 \(x_b\) 连 \((1,0)\) 的边
跑最大费用最大流
3.【代码】
#include<bits/stdc++.h>
#define X(i) (i)
#define Y(i) (i+n)
#define inf 1e9
#define stds(i) (i<=n?i:i-n)
using namespace std;
const int N=510,M=1e5+10;int n;
int S,T;
map<int,string> name;
map<string,int> id;
int h[N],to[M],w[M],ne[M],cost[M],idx=1;
void add(int a,int b,int c,int d){
to[++idx]=b;ne[idx]=h[a];w[idx]=c;h[a]=idx;cost[idx]=d;
to[++idx]=a;ne[idx]=h[b];w[idx]=0;h[b]=idx;cost[idx]=-d;
}
int dis[N],vis[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
}
queue<int> q;dis[S]=0;q.push(S);vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
now[u]=i;
int v=to[i];
if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
void dfss(int u){
if(u!=S&&u!=X(1)&&u!=Y(1)) vis[u]=1;
if(1<=u&&u<=n) cout<<name[u]<<"\n";
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]==0&&stds(u)<=stds(v)){
dfss(v);
break;
}
}
}
void dfst(int u){
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]==0&&!vis[v]&&stds(u)<=stds(v)){
dfst(v);
break;
}
}
if(1<=u&&u<=n) cout<<name[u]<<"\n";
}
int main(){
//freopen("in.txt","r",stdin);
int m;cin>>n>>m;
T=n+n+1;
bool check=0;
for(int i=1;i<=n;i++){
string s;cin>>s;
name[i]=s;
id[s]=i;
}
add(S,X(1),2,0);add(Y(n),T,2,0);
add(X(1),Y(1),2,1);add(X(n),Y(n),2,1);
for(int i=2;i<n;i++) add(X(i),Y(i),1,1);
while(m--){
string a,b;cin>>a>>b;
int aa=min(id[a],id[b]),bb=max(id[a],id[b]);
if(aa==1&&bb==n) check=1;
add(Y(aa),X(bb),1,0);
}
for(int i=2;i<=idx;i++) cost[i]=-cost[i];
Dinic();
if(w[2]==2){
cout<<"No Solution!\n";
return 0;
}
else if(w[2]==1){
if(!check) cout<<"No Solution!\n";
else cout<<2<<"\n"<<name[1]<<"\n"<<name[n]<<"\n"<<name[1]<<"\n";
return 0;
}
cout<<-mincost-2<<"\n";
dfss(S);
dfst(S);
return 0;
}
4.【总结】
- 入点向出点连的边,表示点的属性,本题中表示(可选次数,对答案贡献)
- 出点向入点连的边,表示边的属性,本题中表示(可选次数,对答案贡献)
十三:【P4013 数字梯形问题】
1.【题意】
2.【解法】
不相交路径
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
(1)【第一问】
拆入点 \(x\) 出点 \(y\) ,入点向出点连 \((1,num)\) 的边
若 \(a,b\) 有边, \(y_a\) 向 \(x_b\) 连 \((1,0)\) 的边
建 \(s,t\) ,跑最大费用最大流
(2)【第二问】
改 \((1,num)\) 为 \((inf,num)\)
(3)【第三问】
改 \((1,num)\) 为 \((inf,num)\)
改 \((1,0)\) 为 \((inf,0)\)
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=5010,M=1e5+10,Q=30;int n,m;
int h[N],to[M],ne[M],w[M],cost[M],idx=1;
int tag[M];
void add(int a,int b,int c,int d){
to[++idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx;cost[idx]=d;
to[++idx]=a;w[idx]=0;ne[idx]=h[b];h[b]=idx;cost[idx]=-d;
}
int S,T,step;
#define X(x,y) ((x-1)*(n+m-1)+y)
#define Y(x,y) (X(x,y)+step)
int mincost;
int dis[N],vis[N],now[N];
bool bfs(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
}
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
vis[v]=1;
q.push(v);
}
}
}
return dis[T]!=inf;
}
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&dis[v]==dis[u]+cost[i]&&!vis[v]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(bfs()){
int f;
while(f=dfs(S,inf)) ans+=f;
}
return ans;
}
int main(){
cin>>m>>n;
step=X(n,n+m-1);S=0;T=step*2+1;
int cnt=m;
for(int i=1;i<=n;i++){
for(int j=1;j<=cnt;j++){
int k;cin>>k;
add(X(i,j),Y(i,j),1,k); tag[idx-1]=1;
if(i!=n){
add(Y(i,j),X(i+1,j),1,0); tag[idx-1]=2;
add(Y(i,j),X(i+1,j+1),1,0); tag[idx-1]=2;
}
}
cnt++;
}
for(int i=1;i<=m;i++) add(S,X(1,i),1,0);
for(int i=1;i<=n+m-1;i++){
add(Y(n,i),T,1,0);
tag[idx-1]=1;
}
for(int i=2;i<=idx;i++) cost[i]=-cost[i];
Dinic();
cout<<-mincost<<"\n";
mincost=0;
for(int i=2;i<=idx;i+=2){
w[i]+=w[i^1];w[i^1]=0;
}
for(int i=2;i<=idx;i++){
if(tag[i]==1) w[i]=inf;
}
Dinic();
cout<<-mincost<<"\n";
mincost=0;
for(int i=2;i<=idx;i+=2){
w[i]+=w[i^1];w[i^1]=0;
}
for(int i=2;i<=idx;i++){
if(tag[i]==2) w[i]=inf;
}
Dinic();
cout<<-mincost<<"\n";
return 0;
}
十四:【P4012 深海机器人问题】
1.【题意】
2.【解法】
这里定义,当 \((a,b)\) 表边权时, \(a\) 为容量, \(b\) 为费用
每个点拆成入点 \(x\) 出点 \(y\) ,入点向出点连 \((inf,0)\) 的边
两个相邻的点 \(a,b\) , \(y_a\) 向 \(x_b\) 连 \((1,价值)(inf,0)\) 两条边
建超级源点 \(s\) ,汇点 \(t\)
跑最大费用最大流
3.【代码】
#include<bits/stdc++.h>
#define inf 1e9
using namespace std;
const int N=1010,M=1e5+10;int n,m;
#define X(i,j) ((i+1)*(m+1)+j)
#define Y(i,j) (X(i,j)+X(n,m))
int h[N],to[M],ne[M],w[M],idx=1,cost[M];
int S,T;
void add(int a,int b,int c,int d){
to[++idx]=b;ne[idx]=h[a];h[a]=idx;w[idx]=c;cost[idx]=d;
to[++idx]=a;ne[idx]=h[b];h[b]=idx;w[idx]=0;cost[idx]=-d;
}
int dis[N],now[N],vis[N];
bool SPFA(){
for(int i=S;i<=T;i++){
dis[i]=inf;
now[i]=h[i];
}
queue<int> q;q.push(S);dis[S]=0;vis[S]=1;
while(q.size()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=h[u];i;i=ne[i]){
int v=to[i];
if(w[i]&&dis[v]>dis[u]+cost[i]){
dis[v]=dis[u]+cost[i];
if(vis[v]) continue;
q.push(v);
vis[v]=1;
}
}
}
return dis[T]!=inf;
}
int mincost;
int dfs(int u,int las){
if(u==T) return las;
int ans=0;
vis[u]=1;
for(int i=now[u];i&&las;i=ne[i]){
int v=to[i];
now[u]=i;
if(w[i]&&!vis[v]&&dis[v]==dis[u]+cost[i]){
int k=dfs(v,min(las,w[i]));
w[i]-=k;
w[i^1]+=k;
las-=k;
ans+=k;
mincost+=k*cost[i];
}
}
vis[u]=0;
if(ans==0) dis[u]=inf;
return ans;
}
int Dinic(){
int ans=0;
while(SPFA()){
int f;
while(f=dfs(S,inf)){
ans+=f;
}
}
return ans;
}
int main(){
int a,b;cin>>a>>b;
cin>>n>>m;
for(int i=0;i<=n;i++){
for(int j=0;j<m;j++){
int k;cin>>k;
add(Y(i,j),X(i,j+1),1,k);
add(Y(i,j),X(i,j+1),inf,0);
}
}
for(int i=0;i<=m;i++){
for(int j=0;j<n;j++){
int k;cin>>k;
add(Y(j,i),X(j+1,i),1,k);
add(Y(j,i),X(j+1,i),inf,0);
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
add(X(i,j),Y(i,j),inf,0);
}
}
S=0,T=Y(n,m)+1;
while(a--){
int k,x,y;cin>>k>>x>>y;
add(S,X(x,y),k,0);
}
while(b--){
int k,x,y;cin>>k>>x>>y;
add(Y(x,y),T,k,0);
}
for(int i=2;i<=idx;i++) cost[i]=-cost[i];
Dinic();
cout<<-mincost<<"\n";
return 0;
}

浙公网安备 33010602011771号