能力提升综合题单Part 8.9.3 费用流

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

d i n i c dinic dinic s p f a spfa spfa费用流,多路增广效率较高(然而平时用的是 E K EK EK

#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 负载平衡问题

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

每个点向旁边的点连边,流量为 i n f inf inf, 费用 1 1 1

每个点向汇点连一条边,流量为 a v e r a g e average average,费用 0 0 0

#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+飞回来时间小于 T T T的,跟汇点连边

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

枚举每一趟航班,如果第 i i i趟的结束时间 + + + i i i j j j的飞行时间 < j <j <j的出发时间, i i i j j j连边
(费用全为 f f f,流量为 i n f inf inf,源点流出的流量为 k k k

#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

拆点,一条费用为 − - 点权,流量为 1 1 1,另外一条费用 0 0 0,流量 i n f inf inf

#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

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

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

#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]网络扩容

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

#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 航空路线问题

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

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

将所有的边全部转化为 西边->东边的单向边,最西和最东的点流量限制为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]交换棋子

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

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

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

一拆三!

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

t i m tim tim为这个格子可被交换的次数

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

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

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

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

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

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

统计移动次数,那自然是格子之间的连边费用为 1 1 1,其他全为 0 0 0,边间流量全为 i n f inf inf

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

#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 火星探险问题

建图很裸,拆点连边即可

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

#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 数字梯形问题

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

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

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

t a s k 3 : 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]通信

咕 咕 咕

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值