ACM暑期集训7

今天主要学习了网络流

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值