今天主要学习了网络流
1.最大流
dinic算法
1 先使用BFS建立层次图,就是求每个点到源点的距离
2 再使用DFS找增广路,对每个点x,找层数比它恰好大1的所有点v进行增广 也就是d[x] + 1= d[v]
3 重复第二步直到没有增广路
4 重复123步直到没有增广路
直接上模板
#include<iostream>
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=10110;
const int maxm=50000;
const int inf=0x3f3f3f3f;
int n,m,tol;
int head[maxn];
struct Edge
{
int to,cap,flow,nex;
Edge(int to,int cap,int flow,int nex):to(to),cap(cap),flow(flow),nex(nex) {}
Edge() {};
}edge[maxm];
int dis[maxn];
void init()
{
memset(head,-1,sizeof(head));
tol=0;
}
void addedge(int u,int v,int cap,int rw=0)
{
edge[tol]=Edge(v,cap,0,head[u]);
head[u]=tol++;
edge[tol]=Edge(u,rw,0,head[u]);
head[u]=tol++;
}
/*
void addedge(int u, int v, int w)
{
Edge E1 = {u, v, w, 0, head[u]};
edge[top] = E1;
head[u] = top++;
Edge E2 = {v, u, 0, 0, head[v]};
edge[top] = E2;
head[v] = top++;
}
*/
bool bfs(int s,int t)
{
memset(dis,-1,sizeof(dis));
dis[s]=0;
queue<int> que;
que.push(s);
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=head[u];i!=-1;i=edge[i].nex)
{
int v=edge[i].to;
if(dis[v]==-1&&edge[i].cap>edge[i].flow)
{
dis[v]=dis[u]+1;
if(v==t) return true;
que.push(v);
}
}
}
return false;
}
int dfs(int x,int t,int cap)
{
if(x==t) return cap;
int flow=0,f;
for(int i=head[x];i!=-1;i=edge[i].nex)
{
int v=edge[i].to;
if(dis[v]==dis[x]+1 && edge[i].cap>edge[i].flow)
{
f=dfs(v,t,min(cap-flow,edge[i].cap-edge[i].flow));
edge[i].flow+=f;
edge[i^1].flow-=f;
flow+=f;
if(flow==cap) break;
}
}
if(!flow) dis[x]=1; //加速
return flow;
}
int dicnic(int s,int t) //起点和终点标号
{
int flow=0,f;
while(bfs(s,t))
while((f=dfs(s,t,inf))>0)
flow+=f;
return flow;
}
int main()
{
return 0;
}
接下来上一道题
有A种饮料,B种食物,C头牛,每头牛Ci可以选择某几种饮料和某几种食物,但最终只能吃一份套餐(一份饮料+一份食物),且每种食物和饮料只能被一头牛吃,问最多能喂多少头牛?
分析:
拆点思想
首先满足:一头牛需要同时有食物和饮料才能计入答案。所以建图 S—饮料—牛—食物--t
然后满足:一头牛只能吃一份套餐。所以拆点
2.最小割
割集:边的集合
选择一些点属于s,另一些点属于t,从s到t的 边就是割集。
最小割:割集的权值和最小
花费最小的代价,让源点到汇点完全不流通
最小割=最大流,因此一些最大流的问题可以转向最小割解决
好吧,最小割不会写(捂脸),只能网上找了。
3.最小费用最大流
问:在前面你已经知道怎么求最大流了,现在每条边运送1单位流量的费用都不同, 让你在达到最大流前提下,求最小的运送费用。
首先要保证最大流,所以肯定要在求增广路的前提之下。
在dinic算法中,建立了层次图,两个点之间的距离就是连接它们的边数,可以理解为 每条边费用都是1
现在每条边运送一单位流量需要不同的费用,所以每条边的费用可以当做距离 每次都用spfa求出源点到汇点的距离,然后更新增广路就可以
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1010;
const int MAXM = 1000100;
const int MAXN_INT = (1 << 29);
struct Edge{
int v, w, c, nxt;
};
struct Node{
int id, v;
};
bool vis[MAXN];
Node pre[MAXN];
Edge edge[MAXN];
int n, m, ecnt, sumFlow;
int head[MAXN], dis[MAXN];
void init(){
ecnt = 0;
memset(edge, 0, sizeof(edge));
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int c, int w){
edge[ecnt].v = v;
edge[ecnt].w = w;
edge[ecnt].c = c;
edge[ecnt].nxt = head[u];
head[u] = ecnt++;
}
bool SPFA(int s, int t, int n){
queue <int> que;
memset(vis, 0, sizeof(vis));
fill(dis, dis + MAXN, MAXN_INT);
vis[s] = true;
dis[s] = 0;
que.push(s);
while(!que.empty()){
int u =que.front();
que.pop();
vis[u] = false;
for(int i = head[u]; i + 1; i = edge[i].nxt){
int v = edge[i].v;
if(edge[i].c && dis[v] > dis[u] + edge[i].c){
dis[v] = dis[u] + edge[i].c;
pre[v].v = u;
pre[v].id = i;
if(!vis[v]){
que.push(v);
vis[v] = true;
}
}
}
}
if(dis[t] == MAXN_INT) return false;
return true;
}
int MCMF(int s, int t, int n){
int flow = 0;
int minCost = 0;
while(SPFA(s, t, n)){
int minFlow = MAXN_INT + 1;
for(int i = t; i != s; i = pre[i].v){
minFlow = min(minFlow, edge[pre[i].id].w);
}
for(int i = t; i != s; i = pre[i].v){
edge[pre[i].id].w -= minFlow;
edge[pre[i].id ^ 1].w += minFlow;
}
minCost += dis[t] * minFlow;
}
sumFlow = flow;
return minCost;
}
int main(){
while(scanf("%d%d", &n, &m) != EOF){
int u, v, c, w;
for(int i = 0; i < m; i++){
scanf("%d%d%d%d", &u, &v, &c, &w);
addEdge(u, v, c, w);
addEdge(v, u, -c, 0);
}
int ans = MCMF(1, n, n);
printf("%d\n", ans);
}
return 0;
}