• P3381 【模板】最小费用最大流
  • P4016 负载平衡问题
  • P4452 [国家集训队]航班安排
  • P2045 方格取数加强版
  • P2050 [NOI2012]美食节
  • P2053 [SCOI2007]修车
  • P2604 [ZJOI2010]网络扩容
  • P2770 航空路线问题
  • P3159 [CQOI2012]交换棋子
  • P3356 火星探险问题
  • P3358 最长k可重区间集问题
  • P4013 数字梯形问题
  • P4015 运输问题
  • P5331 [SNOI2019]通信

1.P3381 【模板】最小费用最大流

类dinicdinicdinic的spfaspfaspfa费用流,多路增广效率较高(然而平时用的是EKEKEK)

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=5e3+5,maxm=5e4+5;
int n,m,s,t,cost,maxflow,vis[maxn],dis[maxn];
struct edge{int v,flow,cost,rev;
};
vector<edge>e[maxm*2];
bool spfa(){memset(vis,0,sizeof(vis));memset(dis,inf,sizeof(dis));dis[s]=0;vis[s]=1;queue<int>q;q.push(s);while(!q.empty()){int u=q.front();q.pop();vis[u]=0;for(int i=0;i<e[u].size();i++){int v=e[u][i].v;int c=e[u][i].cost;if(dis[v]>dis[u]+c&&e[u][i].flow){dis[v]=dis[u]+c;if(vis[v]==0){q.push(v);vis[v]=1;}}}}if(dis[t]!=inf)return true;return false;
}
int dfs(int u,int flow){if(u==t){vis[t]=1;maxflow+=flow;return flow;}int used=0;vis[u]=1;for(int i=0;i<e[u].size();i++){int v=e[u][i].v;int c=e[u][i].cost;if((vis[v]==0||v==t)&&e[u][i].flow!=0&&dis[v]==dis[u]+c){int minflow=dfs(v,min(flow-used,e[u][i].flow));if(minflow!=0){cost+=c*minflow;e[u][i].flow-=minflow;e[v][e[u][i].rev].flow+=minflow;used+=minflow;if(used==flow)break;}}}return used;
}
int mcmf(){while(spfa()){vis[t]=1;while(vis[t]){memset(vis,0,sizeof(vis));dfs(s,inf);}}return maxflow;
}
int main(){scanf("%d%d%d%d",&n,&m,&s,&t);int u,v,f,w;for(int i=1;i<=m;i++){scanf("%d%d%d%d",&u,&v,&f,&w);e[u].push_back((edge){v,f,w,e[v].size()});e[v].push_back((edge){u,0,-w,e[u].size()-1});}mcmf();printf("%d %d",maxflow,cost);return 0;
}

2.P4016 负载平衡问题

源点向每个点连一条边,流量为货量,费用000

每个点向旁边的点连边,流量为infinfinf, 费用111

每个点向汇点连一条边,流量为averageaverageaverage,费用000

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f,maxn=1005,maxm=100005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn];
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
int maxflow,mincost;
void mcmf(int s,int t){maxflow=0;mincost=0;while(spfa(s,t)){int u=t;
//      printf("maxflow==%d %d %d\n",maxflow,flow[t],flow[t]*dis[t]);maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return;
}
signed main(){cin>>n;int s=n+1;int t=n+2;int x=0,sum=0;for(int i=1;i<=n;i++){cin>>x;sum+=x;add_edge(s,i,0,x);if(i!=n){add_edge(i,i+1,1,inf);}else{add_edge(n,1,1,inf);}if(i!=1){add_edge(i,i-1,1,inf);}else{add_edge(1,n,1,inf);}}int av=sum/n;for(int i=1;i<=n;i++){add_edge(i,t,0,av);}mcmf(s,t);cout<<mincost;return 0;
}

3.P4452 [国家集训队]航班安排

对于每趟航班:

拆点 费用−q-q−q

到达时间t+t+t+飞回来时间小于TTT的,跟汇点连边

出发时间晚于 从出发点到此机场的时间,源点向其连边

枚举每一趟航班,如果第 iii趟的结束时间+++ iii到jjj的飞行时间<j<j<j的出发时间,iii和jjj连边
(费用全为fff,流量为infinfinf,源点流出的流量为kkk)

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
int t[205][205],f[205][205];
struct node{int a,b,s,t,c;
}q[505];
int main(){cin>>n>>m>>k>>T;for(int i=0;i<n;i++){for(int j=0;j<n;j++){scanf("%d",&t[i][j]);}}for(int i=0;i<n;i++){for(int j=0;j<n;j++){scanf("%d",&f[i][j]);}}for(int i=0;i<m;i++){scanf("%d%d%d%d%d",&q[i].a,&q[i].b,&q[i].s,&q[i].t,&q[i].c);}int tt=2*m+1;int st=2*m+2;int ed=2*m+3;for(int i=0;i<m;i++){add_edge(i,i+m,-q[i].c,1);if(t[q[i].b][0]+q[i].t<=T){add_edge(i+m,ed,f[q[i].b][0],inf);}else continue;if(q[i].s>=t[0][q[i].a]){add_edge(tt,i,f[0][q[i].a],inf);}for(int j=0;j<m;j++){if(t[q[i].b][q[j].a]+q[i].t<=q[j].s){add_edge(i+m,j,f[q[i].b][q[j].a],inf);}}}add_edge(st,tt,0,k);pii yaoyao=mcmf(st,ed);cout<<-yaoyao.second;return 0;
}

4.P2045 方格取数加强版

每个点和右&下的点相连,流量为inf,费用0

拆点,一条费用为 −-−点权,流量为111,另外一条费用000,流量infinfinf

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f,maxn=100005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn];
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
int maxflow,mincost;
void mcmf(int s,int t){while(spfa(s,t)){int u=t;
//      printf("maxflow==%d %d %d\n",maxflow,flow[t],flow[t]*dis[t]);maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return;
}
inline int id(int x,int y){return (x-1)*n+y;
}
signed main(){cin>>n>>k;m=n*n;int m2=2*m+n;int s=m*4+1;int t=m*4+2;int c;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>c;add_edge(id(i,j),id(i,j)+m,-c,1);add_edge(id(i,j),id(i,j)+m2,0,inf);}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(j+1<=n){add_edge(id(i,j)+m,id(i,j+1),0,inf);add_edge(id(i,j)+m2,id(i,j+1),0,inf);}if(i+1<=n){add_edge(id(i,j)+m,id(i+1,j),0,inf);add_edge(id(i,j)+m2,id(i+1,j),0,inf);}}}add_edge(s,id(1,1),0,k);add_edge(id(n,n)+m,t,0,k);add_edge(id(n,n)+m2,t,0,k);mcmf(s,t);cout<<-mincost;return 0;
}

5.P2050 [NOI2012]美食节

核心:动态加边

构建一个二分图,菜在左边,厨师在右边

将所有的菜和对应初始连边,费用为做这道菜的时间,流量为1

在跑费用流的过程中,记录是哪个厨师被征用了,并记录这是这个厨师做的第几道菜,每条边的费用为边权∗time*time∗time,流量为1,

为什么这样是对的呢?其实这里是一个逆向思维,先做的菜等的,所有人等时间就是加倍的,所以时间 最久的菜应该要放到最后做才是最优的,优先选了做的时间少的菜,但是又会疑惑了(比如我,想了一晚上):先选的菜花的单位时间少,为什么还放在后面选,让后面做的时间久的菜等的时间翻倍?这个疑惑还是因为对网络流的理解不够,因为网络流是会自带修正的(反向边的作用),所以加上所有的边之后,最终跑出来的费用是会重新选择用时最短的菜∗n*n∗n,次短∗n−1*n-1∗n−1,这样依此类推

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1,tim[maxn],now,sum;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
int mp[maxn][maxn],p[maxn];
int jl[maxn];
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];int j=jl[pre[t]];
//      printf("j==%d\n",j);tim[j]++;now++;for(int i=1;i<=n;i++){add_edge(i,now,mp[i][j]*tim[j],1);}add_edge(now,t,0,1);jl[now]=j;while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
inline int id(int x,int y){return (x-1)*sum+y+sum;
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>p[i];sum+=p[i];}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp[i][j];}}int num=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){jl[j+sum]=j;add_edge(i,j+sum,mp[i][j],1);}}int s=sum+m+1;int t=sum+m+2;now=t;for(int i=1;i<=n;i++){add_edge(s,i,0,p[i]);}for(int i=1;i<=m;i++){tim[i]=1;add_edge(i+sum,t,0,1);}pair<int,int>yaoyao=mcmf(s,t);cout<<yaoyao.second;return 0;
}

6.P2053 [SCOI2007]修车

上一题的暴力版,直接枚举所有厨师的所有时间即可

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
int mp[maxn][maxn];
inline int id(int x,int y){return (x-1)*n+y+n;
}
int main(){cin>>m>>n;int s=n+n*m+1;int t=s+1;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp[i][j];}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){for(int k=1;k<=n;k++){add_edge(i,id(j,k),mp[i][j]*k,1);}}}for(int i=1;i<=n;i++){add_edge(s,i,0,1);}for(int i=1;i<=n*m;i++){add_edge(i+n,t,0,1);}pair<int,int>yaoyao=mcmf(s,t);printf("%.2f",(double)yaoyao.second/n);return 0;
}

7.P2604 [ZJOI2010]网络扩容

先所有边费用为000跑一次费用流,加上带费用的边再跑一次流量为kkk的费用流即可

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
int u[maxn],v[maxn],w[maxn];
int main(){cin>>n>>m>>k;for(int i=1;i<=m;i++){int c;scanf("%d%d%d%d",&u[i],&v[i],&c,&w[i]);add_edge(u[i],v[i],0,c);}pair<int,int>ans=mcmf(1,n);cout<<ans.first<<" ";add_edge(n,n+1,0,k);for(int i=1;i<=m;i++){add_edge(u[i],v[i],w[i],inf);}pair<int,int>yaoyao=mcmf(1,n+1);cout<<yaoyao.second;return 0;
}

8.P2770 航空路线问题

这题需要转化思维,将一去一回转化成两条不相交的去路

我本想用交换棋子那题一拆三一进一出这样的写,想直接一步到位,结果无限RERERE…

将所有的边全部转化为 西边->东边的单向边,最西和最东的点流量限制为2,其他点全部为1,拆点的费用全为-1

跑完费用流跑两次dfs,一次先序输出,一次后序输出

特判1->n->1,直接输出

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
string str[105];
unordered_map<string,int>mp;
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;int tim=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];++tim;while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
int vis[1005];
vector<string>jl1,jl2;
void dfs1(int u){jl1.push_back(str[u]);
//  cout<<str[u]<<endl;vis[u]=1;for(int i=head[u+n];i;i=e[i].nex){int v=e[i].v;if(e[i].flow!=0||vis[v]==1||v>n||v<1)continue;else{dfs1(v);break;}}
}
void dfs2(int u){//  vis[u]=1;jl2.push_back(str[u]);for(int i=head[u+n];i;i=e[i].nex){int v=e[i].v;if(e[i].flow!=0||vis[v]==1||v>n||v<1)continue;dfs2(v);}
//  cout<<str[u]<<endl;
}
int main(){cin>>n>>m;int num=0;int s=1;int t=n*2;bool ok=false;for(int i=1;i<=n;i++){string a;cin>>a;mp[a]=i;str[i]=a;if(i==1||i==n){add_edge(i,i+n,-1,2);}else{add_edge(i,i+n,-1,1);}}for(int i=1;i<=m;i++){string a,b;cin>>a>>b;int x=mp[a];int y=mp[b];if(x>y)swap(x,y);if(x==1&&y==n)ok=true;add_edge(x+n,y,0,1);}
//  add_edge(s,s+n,0,2);
//  add_edge(n+n,t,0,2);pii yaoyao=mcmf(s,t);if(yaoyao.first==2){printf("%d\n",-yaoyao.second-2);dfs1(s);dfs2(s);for(auto v:jl1){cout<<v<<endl;}string zz[105];int numzz=0;for(auto v:jl2){zz[++numzz]=v;}for(int i=numzz;i>=1;i--){cout<<zz[i]<<endl;}}else if(yaoyao.first==1&&ok){cout<<2<<endl;cout<<str[1]<<endl;cout<<str[n]<<endl;cout<<str[1]<<endl;}else{printf("No Solution!");}return 0;}

9.P3159 [CQOI2012]交换棋子

刚看想的太简单,直接莽了一发,意料之中的只过了样例

首先题意要读清楚,相邻!!!相邻的八个点都可以换

然后这一点就有点骚了,因为每个格子有换的次数限制,然后起始点只会换一次,但是路上的点都会被换两次,我们必须要处理这个区别,咋办呢?一拆二好像并不能解决这个问题,所以

一拆三!

每个点拆为原点,入点和出点,入点向原点连边,原点向出点连边

timtimtim为这个格子可被交换的次数

1.1.1.如果一个点为起始点,最终不为终点:

入点->原点,流量为tim/2tim/2tim/2,原点->出点 (tim+1)/2(tim+1)/2(tim+1)/2

2.2.2.如果一个点为终点,最开始不为起始点:

入点->原点,流量为(tim+1)/2(tim+1)/2(tim+1)/2,原点->出点 tim/2tim/2tim/2

3.3.3.如果一个点既为起点又为终点:

入点->原点,流量为tim/2tim/2tim/2,原点->出点 tim/2tim/2tim/2

统计移动次数,那自然是格子之间的连边费用为111,其他全为000,边间流量全为infinfinf

考虑一个点被交换的次数为奇数的情况,比如555,如果作为出点,考虑第一类点,必须作为出发点,所以至少有点要从它这里跑出去一次,然后就可以作为中间点了,所以它的出发流量为333,以此类推,第二类点至少被跑进来一次,然后当中间点,finefinefine

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
const int dx[]={1,1,1,-1,-1,-1,0,0};
const int dy[]={1,-1,0,1,-1,0,1,-1};
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
inline int id1(int x,int y){return (x-1)*m+y;
}
inline int id2(int x,int y){return (x-1)*m+y+n*m;
}
inline int id3(int x,int y){return (x-1)*m+y+n*m*2;
}
char mp1[25][25],mp2[25][25],mp3[25][25];
int u[maxn],v[maxn],w[maxn];
int main(){cin>>n>>m;int s=n*m*3+1;int t=n*m*3+2;int cc=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp1[i][j];if(mp1[i][j]=='1')add_edge(s,id2(i,j),0,1),cc++;}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp2[i][j];if(mp2[i][j]=='1')add_edge(id2(i,j),t,0,1);}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp3[i][j];int tim=mp3[i][j]-'0';if(mp1[i][j]==mp2[i][j]){add_edge(id1(i,j),id2(i,j),0,tim/2);add_edge(id2(i,j),id3(i,j),0,tim/2);}if(mp1[i][j]=='1'&&mp2[i][j]=='0'){add_edge(id1(i,j),id2(i,j),0,tim/2);add_edge(id2(i,j),id3(i,j),0,(tim+1)/2);}if(mp1[i][j]=='0'&&mp2[i][j]=='1'){add_edge(id1(i,j),id2(i,j),0,(tim+1)/2);add_edge(id2(i,j),id3(i,j),0,tim/2);}}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){for(int k=0;k<8;k++){int xx=dx[k]+i;int yy=dy[k]+j;if(xx>=1&&xx<=n&&yy<=m&&yy>=1){add_edge(id3(i,j),id1(xx,yy),1,inf);}}}}pair<int,int>yaoyao=mcmf(s,t);int ans=yaoyao.first;
//  cout<<ans<<" ";if(ans==cc)cout<<yaoyao.second;else cout<<"-1";return 0;
}

10.P3356 火星探险问题

建图很裸,拆点连边即可

重点在于输出路径,这里需要用到边的流量的性质,正向边走过的流量就在反向边上加上了,我们可以由此来判断每个点被多少条路线走过,dfsdfsdfs记录走过每个点的次数,每次与反向边的流量比较即可

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
inline int id(int x,int y){return (x-1)*m+y;
}
inline int id2(int x,int y){return (x-1)*m+y+n*m;
}
int vis[maxn];
void dfs(int u,int num){for(int i=head[u+n*m];i;i=e[i].nex){int v=e[i].v;if(v==u+1&&vis[i]<e[i^1].flow){printf("%d 1\n",num);vis[i]++;dfs(v,num);break;}if(v==u+m&&vis[i]<e[i^1].flow){printf("%d 0\n",num);vis[i]++;dfs(v,num);break;}}
}
int mp[105][105];
int main(){cin>>k>>m>>n;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp[i][j];if(mp[i][j]==1)continue;add_edge(id(i,j),id2(i,j),mp[i][j]==2?-1:0,1);add_edge(id(i,j),id2(i,j),0,inf);}}int s=n*m*2+1;int t=s+1;if(mp[1][1]!=1)add_edge(s,id(1,1),0,k);if(mp[n][m]!=1)add_edge(id2(n,m),t,0,k);for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(mp[i][j]==1)continue;if(mp[i][j+1]!=1&&j+1<=m){add_edge(id2(i,j),id(i,j+1),0,inf);}if(mp[i+1][j]!=1&&i+1<=n){add_edge(id2(i,j),id(i+1,j),0,inf);}}}pii yaoyao=mcmf(s,t);for(int i=1;i<=k;i++){dfs(1,i);}return 0;
}

11.P3358 最长k可重区间集问题

先咕一下

12.P4013 数字梯形问题

憨憨题,大概是考验对拆点的理解?

task1:task1:task1:不允许有路径相交:
很简单,一看就是点和边流量限制都为1,这样就不可能有右边的流跨到左边去了

task2:task2:task2:允许选重复的点:
点的流量限制无限即可,主要点跟汇点的流量也要改掉

task3:task3:task3:允许路径相交,允许重复点:
点边都无限即可

建图三次

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
inline int id(int x,int y){return (x-1)*(m+n-1)+y;
}
int mp[1000][1000];
int main(){cin>>m>>n;int num=10000;int k=m-1;int s=20001;int t=20002;for(int i=1;i<=n;i++){k++;for(int j=1;j<=k;j++){cin>>mp[i][j];add_edge(id(i,j),id(i,j)+num,-mp[i][j],1);if(i==1){add_edge(s,id(i,j),0,1);}if(i==n){add_edge(id(i,j)+num,t,0,1);}}}k=m-1;for(int i=1;i<=n-1;i++){k++;for(int j=1;j<=k;j++){add_edge(id(i,j)+num,id(i+1,j),0,1);add_edge(id(i,j)+num,id(i+1,j+1),0,1);}}pii yaoyao1=mcmf(s,t);cout<<-yaoyao1.second<<endl;memset(head,0,sizeof head);cnt=1;for(int i=1;i<=n;i++){k++;for(int j=1;j<=k;j++){//    cin>>mp[i][j];add_edge(id(i,j),id(i,j)+num,-mp[i][j],inf);if(i==1){add_edge(s,id(i,j),0,1);}if(i==n){add_edge(id(i,j)+num,t,0,inf);}}}k=m-1;for(int i=1;i<=n-1;i++){k++;for(int j=1;j<=k;j++){add_edge(id(i,j)+num,id(i+1,j),0,1);add_edge(id(i,j)+num,id(i+1,j+1),0,1);}}pii yaoyao2=mcmf(s,t);cout<<-yaoyao2.second<<endl;memset(head,0,sizeof head);cnt=1;for(int i=1;i<=n;i++){k++;for(int j=1;j<=k;j++){// cin>>mp[i][j];add_edge(id(i,j),id(i,j)+num,-mp[i][j],inf);if(i==1){add_edge(s,id(i,j),0,1);}if(i==n){add_edge(id(i,j)+num,t,0,inf);}}}k=m-1;for(int i=1;i<=n-1;i++){k++;for(int j=1;j<=k;j++){add_edge(id(i,j)+num,id(i+1,j),0,inf);add_edge(id(i,j)+num,id(i+1,j+1),0,inf);}}pii yaoyao3=mcmf(s,t);cout<<-yaoyao3.second<<endl;return 0;
}

13.P4015 运输问题

迷惑题目

裸跑一次最大和一次最小费用流即可

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){e[++cnt].v=v;e[cnt].cost=cost;e[cnt].flow=flow;e[cnt].nex=head[u];head[u]=cnt;e[++cnt].v=u;e[cnt].cost=-cost;e[cnt].flow=0;e[cnt].nex=head[v];head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){queue<int>q;memset(dis,0x3f,sizeof dis);memset(flow,0x3f,sizeof flow);memset(inque,0,sizeof inque);q.push(s);inque[s]=1;dis[s]=0;pre[t]=-1;while(!q.empty()){int u=q.front();q.pop();inque[u]=0;for(int i=head[u];i;i=e[i].nex){int v=e[i].v;if(dis[v]>dis[u]+e[i].cost&&e[i].flow){dis[v]=dis[u]+e[i].cost;pre[v]=u;last[v]=i;flow[v]=min(flow[u],e[i].flow);if(!inque[v]){q.push(v);inque[v]=1;}}}}if(pre[t]!=-1)return true;return false;
}
unordered_map<string,int>mp;
inline pair<int,int> mcmf(int s,int t){int maxflow=0;int mincost=0;while(spfa(s,t)){int u=t;maxflow+=flow[t];mincost+=flow[t]*dis[t];while(u!=s){e[last[u]].flow-=flow[t];e[last[u]^1].flow+=flow[t];u=pre[u];}}return {maxflow,mincost};
}
inline int id(int i,int j){return (i-1)*n+j;
}
int a[50005],b[50005],x[50005];
int main(){cin>>m>>n;int s=n+m+1;int t=n+m+2;for(int i=1;i<=m;i++){scanf("%d",&a[i]);add_edge(s,i,0,a[i]);}for(int i=1;i<=n;i++){scanf("%d",&b[i]);add_edge(i+m,t,0,b[i]);}for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){scanf("%d",&x[id(i,j)]);add_edge(i,j+m,x[id(i,j)],a[i]);}}pii yaoyao=mcmf(s,t);cout<<yaoyao.second<<endl;cnt=1;memset(head,0,sizeof head);for(int i=1;i<=m;i++){add_edge(s,i,0,a[i]);}for(int i=1;i<=n;i++){add_edge(i+m,t,0,b[i]);}for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){add_edge(i,j+m,-x[id(i,j)],a[i]);}}pii yaoyao2=mcmf(s,t);cout<<-yaoyao2.second<<endl;return 0;
}

14.P5331 [SNOI2019]通信

咕 咕 咕

能力提升综合题单Part 8.9.3 费用流相关推荐

  1. 洛谷 能力提升综合题单Part1 入门阶段 P1089 津津的储蓄计划 带注释

    题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津300300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同. 为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在 ...

  2. 刷题总结——支线剧情(bzoj3876费用流)

    题目: [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情.这些游戏往往 都有很多的支线剧情,现在JYY想花费最 ...

  3. 线性规划与网络流24题 运输问题(最裸的费用流了)

    存费用流模板 用sfpa算出最小费用和路径,沿这条路径增广 1 const 2 inf=maxlongint; 3 var 4 n,m:longint; 5 map,a,w:array[0..120, ...

  4. [网络流24题][CODEVS1916]负载平衡问题(费用流)

    题目描述 传送门 题解 首先拆点,分XiYi,对应每个仓库. 从源点向Xi连边,容量为ri,费用为0: 从Yi向汇点连边,容量为xba,费用为0: 从Xi向对应的Yi连边,容量为INF,费用为0: 从 ...

  5. [网络流24题][CODEVS1237]餐巾计划问题(费用流)

    题目描述 传送门 题解 拆点,把每天的点拆成xi和yi,xi表示每一天的脏毛巾,yi表示每一天的新毛巾. 从超级源向xi连边,容量为ri,费用为0: 从yi向超级汇连边,容量为ri,费用为0: 从超级 ...

  6. 辣鸡HellPix刷yyb网络流题单(orz yyb)

    题目 评价 状态 [USACO4.2]草地排水Drainage Ditches(最大流)  最大流模板 Accepted HDU 3416 Marriage Match IV(最短路,网络流)  最小 ...

  7. 蔡丹红老师刁酒集团《基层管理人员综合能力提升培训班》企业内训开讲

    蔡丹红老师:刁酒集团<基层管理人员综合能力提升培训班>企业内训开讲 一线员工总是奋斗在前线,为公司打开销路.打开渠道,为公司创造业绩,作为基层管理人员应做好本职工作,努力提升自己,提高综合 ...

  8. 软件技术专业大学生该如何制定职业综合能力提升计划

    记得那还是五年级的时候,那时班主任叫我们写上自己的理想吧.我就记得自己写了成为一个对社会有用的人,而别的同学都写上科学家和老师等等.但随着不断的成长,我们都走上了自己特定的人生轨道.这个时候我们不能再 ...

  9. 《大数据实践课》开创实践教学新模式:清华大数据能力提升项目特色课程系列报道之一

    2014年4月,清华大学顺应时代潮流成为全国第一批成立大数据研究机构的高等学府.四年来,清华-青岛数据科学研究院(以下简称:数据院)与研究生院共同设计组织实施了以大数据能力提升项目为主的大数据人才培养 ...

  10. 计算机网络技术综合题大全

    第1章 计算机网络概述 一.简答题 1.简述计算机网络的发展过程. 答:从1946年世界上第一台计算机ENIAC的诞生到现在网络的全面普及,计算机网络的发展大体可以分为以下4个阶段: (1)第一代计算 ...

最新文章

  1. mirna富集分析_2020年的3+分ceRNA分析长啥样?
  2. epplus保存为流_c# – 另存为使用EPPlus?
  3. 转[WinForm] VS2010发布、打包安装程序(超全超详细)
  4. 如何使用Restic Backup Client将数据备份到对象存储服务
  5. 设计模式(七)装饰模式
  6. easyui table 如何只展示一条_如何使用MySQL,这些操作你得明白!
  7. 调试程序Bug-陈棚
  8. win11怎么退回win7 Windows11退回win7的步骤方法
  9. 红警2 csf文件解析 简体化
  10. 文字转语音怎么真人发声
  11. 音频格式转换器哪个好,推荐几款免费的mp3格式转换器
  12. 7天从代码入门到开发应用,怎样快速提高代码能力?
  13. 1674386-82-3,Lipoamido-PEG2-alcohol醇基可以反应进一步衍生化合物
  14. 论文笔记 Object-Aware Instance Labeling for Weakly Supervised Object Detection - ICCV 2019
  15. Tower of Hanoi (汉诺塔问题)
  16. idea插件 之~~~~mybatisx(忍者鸟)、lombox(小辣椒)
  17. AppScan使用教程
  18. 页面加载时,vue生命周期的触发顺序
  19. cass怎么添加指北针图例_怎么才能在excel中把表格做的好看?
  20. Hive中ORDER BY、SORT BY和DISTRIBUTE BY

热门文章

  1. php中的条件语句,PHP中的条件语句和示例
  2. java怎么返回string_黄瓜Java-如何在下一步中使用返回的String?
  3. python多进程优化_python多进程提高cpu利用率
  4. python分布式爬虫_python-分布式爬虫
  5. python 复制列表内容_python 复制列表的六种方法
  6. 磁盘上没有足够的空间完成此操作_Win10硬盘怎么分区?Win10系统下新建磁盘分区图解教程...
  7. PAT之查找:遍历、二分、hash
  8. nyoj 236 心急的C小加(贪心)
  9. nyoj810 贪心的嘿嘿(想弄死这出题的)
  10. 服务器位置设置,服务器部署位置