网络流

好东西,可以学

2022.3.25更新

确实是好东西,下次再更

2022.3.25晚

总有人抱怨我写的太认真/lh,我不会markdown我也很无奈啊/yun。

网络流24题网上比我写的好的很多,我就不献丑了,接下来我们就讲讲最小割吧

例题1:【模板】最小割

好的你已经掌握了最小割的全部内容,接下来就拿几题练练手吧!

练习一:[THUPC2022 初赛] 分组作业

相信聪明的你已经做出来了,那么我说说我的做法。
考虑最小割的本质
将每个点赋0,1值,我们称之为\(w_i\),我们要求的就是\(w_S=1,w_T=0\)情况下,$$\min({a_{i,j} \times\max(w_i-w_j,0)})$$
什么是最小割呢?所谓最小割,就是最小的隔断,至于为什么是最小的隔断,小编也很惊讶。
以上就是有关最小割的所有内容,欢迎评论区讨论吧!

~~~~~~~~~~~~~~~~~~~~开玩笑的~~~~~~~~~~~~~~~~~

对于这个模型,我们举个栗子来理解一下

\(n\) 个物品和两个集合 \(A,B\) ,如果一个物品放入 \(A\) 集合会花费 \(a_i\),放入 \(B\) 集合会花费 \(b_i\) ;还有若干个形如 \(u_i,v_i,x_i\) 限制条件,表示如果\(u_i\)\(v_i\) 同时不在一个集合会花费\(x_i\)。每个物品必须且只能属于一个集合,求最小的代价。

这是一个经典的 二者选其一 的最小割题目,解法相信大家都知道。我们设置源点 \(S\)和汇点\(T\) ,第 \(i\) 个点由 \(S\) 连一条容量为 \(a_i\) 的边、向 \(T\) 连一条容量为\(b_i\) 的边。对于限制条件 \(u_i,v_i,x_i\),我们在\(u_i,v_i\) 之间连容量为 \(x_i\) 的双向边。
如果用刚刚那个公式理解的话。先不考虑物品间的影响,\(w_i=0\)可以认为物品在A中,这时候答案加了\(a_i\),正好对应\(w_S-w_i=1\)答案加了\(a_i\)\(w_i=1\)时同理。
再来考虑物品间的影响,发现如果两个物品不在同一个集合的话,\(w_i\)是不同的,又因为连的是双向边,因此答案会加\(x_i\)

个人感觉相比一条条看哪条边被割,这种理解方式更简单,不容易出错
只要定好了\(w_i\)的意义,根据题面的要求连边,一些题目还是很简单的,比如这题/cy

回到这道题,通过上述例子,相信大家已经对这个模型有所了解,这道题可以说是这个模型的入门题
我们定两类点:团队点和个人点。\(w_i=1\)代表合作。
然后连边就好了,是不是很简单/yiw
唯一有点意思的就是如何限制团队点和个人点的关系,我们发现团队点的\(w\)值一定小于个人点,因此我们可以将团队点向个人点连流量为无限大的边,这道题就做完了

我们发现一个小技巧:我们如果想让\(w_i<=w_j\),只要\(i\)\(j\)连一条流量无限大的边即可

code:

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
inline int read () {
    int x = 0, f = 1, ch = getchar();
    while(!isdigit(ch)) { if(ch == '-') f = -f; ch = getchar(); }
    while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
const int N=16005;
int n,m,head[N],k=1,dep[N],S,T,o=(1<<30);
struct Node{
    int nex,to,w;
}e[N*50];
void add(int u,int v,int w){
    e[++k].to=v;
    e[k].nex=head[u];
    e[k].w=w;
    head[u]=k;
}
void ad(int u,int v,int w){
    add(u,v,w);add(v,u,0);
}
queue<int> q;
bool bfs(){
	memset(dep,0,sizeof(dep));
	q.push(S);
	dep[S]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for (int i=head[u];i;i=e[i].nex){
			int v=e[i].to;
			if(e[i].w&&!dep[v]){
				dep[v]=dep[u]+1;
				q.push(v);
			}
		} 
	}
	if(dep[T]) return 1;
	return 0;
}
int dfs(int u,int in){
	if(u==T) return in;
	int out=0;
	for (int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(e[i].w&&dep[v]==dep[u]+1){
			int res=dfs(v,min(in,e[i].w));
			in-=res;
			out+=res;
			e[i].w-=res;
			e[i^1].w+=res;
		}
	}
	if(out==0) dep[u]=0;
	return out;
}
int team(int i){
    return (i+1)/2+2*n;
}
signed main (){
    // freopen("in.in", "r", stdin);
    // freopen("out.out", "w", stdout);
    n=read();m=read();
    S=3*n+1;T=S+1;
    for (int i=1;i<=2*n;i++){
        int c=read(),d=read(),e=read();
        if(i&1) ad(i,i+1,e);
        else ad(i,i-1,e);
        ad(S,i,d);ad(i,T,c);
        ad(team(i),i,o);
    }
    for (int i=1;i<=m;i++){
        int A=read(),B=read(),a=read(),b=read();
        ad(B,team(A),a);
        ad(team(B),A,b);
    }
    int ans=0;
    while(bfs()) ans+=dfs(S,o);
    printf("%lld\n",ans);
    return 0;
}

咕着,遇到好题再更

posted @ 2022-03-24 19:35  April_lie  阅读(120)  评论(5)    收藏  举报