【网络流杂谈】

简介:

快要省选了,现在啥也不会的菜鸡最后的挣扎
这个星期给自己的任务是搞懂网络流
这个星期的东西都更在这个贴里了

题目:

一:[SCOI2007]蜥蜴

[SCOI2007]蜥蜴

思路:
考虑建图,主要是将一个点拆成两个点,入点和出点间连边,这条边的权值可以限制这条边的走的次数

代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#define ll long long

int r,c,d;
int h[50][50],f[50][50];

int head[100000];

struct P{
	int to,next,v;
}e[100000];

int s,t;

int cnt = -1;

void add(ll x,ll y,ll v){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = v;
	head[x] = cnt;
}

double get(ll x1,ll y1,ll x2,ll y2){
	return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

int sum;

int vis[10000];

bool bfs(){
	std::queue<int>q;
	q.push(s);
	std::memset(vis,-1,sizeof(vis));
	vis[s] = 1;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		for(int i = head[u];i != -1;i = e[i].next){
			int v = e[i].to;
			if(e[i].v && vis[v] == -1){
				vis[v] = vis[u] + 1;
				q.push(v);
			} 
		}
	}
	return vis[t] != -1;
}

int dfs(int u,int flow){
	if(u == t) return flow;
	int used = 0,w;
	for(int i = head[u];i != -1;i = e[i].next){
		int v = e[i].to;
		if(vis[v] == vis[u] + 1 && e[i].v){
			w = dfs(v,std::min(flow - used,e[i].v));
			e[i].v -= w;
			e[i ^ 1].v += w;
			used += w;
			if(used == flow) return used;
		}
	}
	if(!used)vis[u] = -1;
	return used;
}

int dinic(){
	int ans = 0;
	while(bfs())ans += dfs(s,0x3f3f3f3f);
	return ans;
}

int main(){
	scanf("%d%d%d",&r,&c,&d);
	memset(head,-1,sizeof(head));
	for(int i = 1;i <= r;++i)
	for(int j = 1;j <= c;++j)
	scanf("%1d",&h[i][j]);
	s = 0,t = 2 * r * c + 1;
	for(int i = 1;i <= r;++i)
	for(int j = 1;j <= c;++j){
		char a = getchar();
		while(a != '.' && a != 'L'){
			a = getchar();
		}
		if(a == 'L')
		f[i][j] = 1;
	}
	int now = 0;
	for(int i = 1;i <= r;++i)
	for(int j = 1;j <= c;++j){
		++now;
		if(h[i][j]){
		add(now,now + r * c,h[i][j]);
		add(now + r * c,now,0);			
		}
		if(f[i][j]){
			add(s,now,1);
			add(now,s,0);
			sum ++ ;
		}
		if((i <= d || i >= r - d + 1) && h[i][j]){
			add(now + r * c,t,0x3f3f3f3f);
			add(t,now + r * c,0);
		}else
		if((j <= d || j >= c - d + 1 )&& h[i][j]){
			add(now + r * c,t,0x3f3f3f3f);
			add(t,now + r * c,0);
		}
	}
	for(int i = 1;i <= r;++i)
	for(int j = 1;j <= c;++j)
	for(int p = 1;p <= r;++p)
	for(int q = 1;q <= c;++q){
		if(get(i,j,p,q) <= (double)d && (i != p || j != q) && h[i][j] && h[p][q]){
			add((i - 1) * c + j + r * c,(p - 1) * c + q,0x3f3f3f3f);
			add((p - 1) * c + q,(i - 1) * c + j + r * c,0);
		}
	}
	std::cout<<sum - dinic()<<std::endl;
}//调了很久,果然我是菜得不行

二: [ICPC-Beijing 2006]狼抓兔子

[ICPC-Beijing 2006]狼抓兔子

思路:说起来还是蛮有感触的
想当年第一次打开\(BZOJ\)看到的就是这个题,当时什么也不会就搜了题解,不过什么也没看懂
当时那个懵懂的人,现在却在为省选准备啊
很明显是最小割,不过也有平面图对偶图最短路路的做法
平面图对偶图:
如果一个图是 \(G\) 平面图,那么其存在一个对偶图 \(G′\) ,构造方法为将原图中的每个面替换成点,点替换为面,新的边与原来的边分别相交。
那么此时有,原图的最大流 = 最小割 = 对偶图最短路。

代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long

int n,m;

int id(int x,int y){return (x - 1) * m + y;}

int head[1000005];

struct P{
	int to,next,v;
}e[6000005];

int s,t;

int cnt = 1;

void add(ll x,ll y,ll v){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = v;
	head[x] = cnt;
}

int sum;

int vis[1000005];

bool bfs(){
	std::queue<int>q;
	q.push(s);
	std::memset(vis,-1,sizeof(vis));
	vis[s] = 1;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		for(int i = head[u];i;i = e[i].next){
			int v = e[i].to;
			if(e[i].v && vis[v] == -1){
				vis[v] = vis[u] + 1;
				q.push(v);
			} 
		}
	}
	return vis[t] != -1;
}

int dfs(int u,int flow){
	if(u == t) return flow;
	int used = 0,w;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(vis[v] == vis[u] + 1 && e[i].v){
			w = dfs(v,std::min(flow - used,e[i].v));
			e[i].v -= w;
			e[i ^ 1].v += w;
			used += w;
			if(used == flow) return used;
		}
	}
	if(!used)vis[u] = -1;
	return used;
}

int dinic(){
	int ans = 0;
	while(bfs())ans += dfs(s,0x3f3f3f3f);
	return ans;
}


int main(){
	scanf("%d%d",&n,&m);
	s = 1,t = id(n,m);
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m - 1;++j){
		ll k;
		scanf("%lld",&k);
		add(id(i,j),id(i,j + 1),k);
		add(id(i,j + 1),id(i,j),k);
	}
	for(int i = 1;i <= n - 1;++i)
	for(int j = 1;j <= m;++j){
		ll k;
		scanf("%lld",&k);
		add(id(i,j),id(i + 1,j),k);
		add(id(i + 1,j),id(i,j),k);
	}
	for(int i = 1;i <= n - 1;++i)
	for(int j = 1;j <= m - 1;++j){
		ll k;
		scanf("%lld",&k);
		add(id(i,j),id(i + 1,j + 1),k);
		add(id(i + 1,j + 1),id(i,j),k);
	}
	std::cout<<dinic();
}

最小费用最大流

把最开始的FF算法的\(bfs\)部分改为\(spfa\),同时记录前缀边,方便进行数据更新(基于\(dinic\)的最小费用流算法过几天再写)
代码

#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long

int n,m,s,t;

struct P{
	int to,next,v,c;
}e[100005];

int head[5005],minw[5005],minc[5005],pre[5005];

bool vis[5005];

int cnt = 1;

void add(int x,int y,int w,int c){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = w;
	e[cnt].c = c;
	head[x] = cnt;
}

bool spfa(){
	std::queue<int>QWQ;
	for(int i = 1;i <= n;++i){
		minc[i] = 0x3f3f3f3f,vis[i] = 0;
	}
	QWQ.push(s);
	vis[s] = 1;
	minw[s] = 0x3f3f3f3f;
	minc[s] = 0;
	while(!QWQ.empty()){
		int now = QWQ.front();
		QWQ.pop();
		vis[now] = 0;
		for(int i = head[now];i;i = e[i].next){
			if(e[i].v){
				int v = e[i].to;
				if(minc[v] > minc[now] + e[i].c){
					minc[v] = minc[now] + e[i].c;
					pre[v] = i;
					minw[v] = std::min(minw[now],e[i].v);
					if(!vis[v])vis[v] = 1,QWQ.push(v);
				}
			}
		}
	}
	return minc[t] != 0x3f3f3f3f;
}

int answ,ansc;

void up(){
	int now = t;
	answ += minw[t];
	ansc += minc[t] * minw[t];
 	while(now != s){
		e[pre[now]].v -= minw[t];
		e[pre[now] ^ 1].v += minw[t];
		now = e[pre[now] ^ 1].to;
	}
}

int main(){
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i = 1;i <= m;++i){
		int u,v,w,c;
		scanf("%d%d%d%d",&u,&v,&w,&c);
		add(u,v,w,c);
		add(v,u,0,-c);
	}
	while(spfa())
	up();
	std::cout<<answ<<" "<<ansc<<std::endl;
}//明白自己深深的弱小

一: [SDOI2013]费用流
[SDOI2013]费用流
还是一样拆点来满足题目要求的次数限制,在拆开的两点间的费用应为\(0\)

代码

#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long

int n,m,s,t;

struct P{
	int to,next,v,c;
}e[100005];

int head[5005],minw[5005],minc[5005],pre[5005];

bool vis[5005];

int cnt = 1;

void add(int x,int y,int w,int c){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = w;
	e[cnt].c = c;
	head[x] = cnt;
}

bool spfa(){
	std::queue<int>QWQ;
	for(int i = 1;i <= 2 * n;++i){
		minc[i] = 0x3f3f3f3f,vis[i] = 0;
	}
	QWQ.push(s);
	vis[s] = 1;
	minc[s] = 0;
	minw[s] = 0x3f3f3f3f;
	while(!QWQ.empty()){
		int now = QWQ.front();
		QWQ.pop();
		vis[now] = 0;
		for(int i = head[now];i;i = e[i].next){
			int v = e[i].to;
			if(e[i].v){
				if(minc[v] > minc[now] + e[i].c){
					minc[v] = minc[now] + e[i].c;
					pre[v] = i;
					minw[v] = std::min(minw[now],e[i].v);
					if(!vis[v])vis[v] = 1,QWQ.push(v);
				}
			}
		}
	}
	return minc[t] != 0x3f3f3f3f;
}

int answ,ansc;

void up(){
	answ += minw[t];
	ansc += minc[t] * minw[t];
	int now = t;
	while(now != s){
		e[pre[now]].v -= minw[t];
		e[pre[now] ^ 1].v += minw[t];
		now = e[pre[now] ^ 1].to;
	}
}

int main(){
	scanf("%d%d",&n,&m);
	s = n + 1,t = n;
	for(int i = 1;i <= n;++i){
		add(i,i + n,1,0);
		add(i + n,i,0,0);
	}
	for(int i = 1;i <= m;++i){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a + n,b,1,c);
		add(b,a + n,0,-c);
	}
	while(spfa())
	up();
	std::cout<<answ<<" "<<ansc<<std::endl;
}//十分钟能敲完这些代码,手速还可以

文理分科

文理分科
一种典型的二选一的题目,可以将两个选择设为\(s,t\),然后分别连边,然后求最小割
对于题目中所说的集体选课多权值的情况,可以虚构\((i,j)'\)然后以\(ss[i][j]为权在他和s之间连边\),然后以\(inf\)\((i,j)自己和周围五个点连边\),这步\(inf\)是为了防止这条边被割掉
然后求最小割,用全部的权值和减去最大流即可

#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long

ll n,m;

ll a[200][200],s1[200][200],sa[200][200],ss[200][200];

int id(int x,int y){return (x - 1) * m + y;}

struct P{
	int to,next,v;
}e[500000];

ll cnt = 1,head[500000];

ll s,t;

void add(ll x,ll y,ll v){
	e[++cnt].to = y;
	e[cnt].v = v;
	e[cnt].next = head[x];
	head[x] = cnt;
}

ll sum = 0;
ll vis[500000];
ll pcnt;
std::queue<ll>QWQ;

bool bfs(){
	for(int i = 1;i <= pcnt;++i)
	vis[i] = -1;
	vis[s] = 1;
	QWQ.push(s);
	while(!QWQ.empty()){
		int u = QWQ.front();
		QWQ.pop();
		for(int i = head[u];i;i = e[i].next){
			int v = e[i].to;
			if(e[i].v && vis[v] == -1){
				vis[v] = vis[u] + 1;
				QWQ.push(v);
			} 
		}
	}
	return vis[t] != -1;
}

ll dfs(ll now,ll flow){
	if(now == t)return flow;
	ll used = 0;
	for(int i = head[now];i;i = e[i].next){
		ll v = e[i].to;
		if(e[i].v && vis[v] == vis[now] + 1){
			ll w = dfs(v,std::min(flow - used,(ll)e[i].v));
			e[i].v -= w;
			e[i ^ 1].v += w;
			used += w;
			if(flow == used)
			break;
		}
	}
	if(!used)vis[now] = -1;
	return used;
}

ll dinic(){
	ll ans = 0;
	while(bfs())
	ans += dfs(s,0x3f3f3f3f);
	return ans;
}

int main(){
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j)
	scanf("%lld",&a[i][j]),sum += a[i][j];
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j)
	scanf("%lld",&s1[i][j]),sum += s1[i][j];
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j)
	scanf("%lld",&sa[i][j]),sum += sa[i][j];
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j)
	scanf("%lld",&ss[i][j]),sum += ss[i][j];
	s = 0,t = n * m + 1;
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		add(s,id(i,j),a[i][j]);
		add(id(i,j),s,0);
		add(id(i,j),t,s1[i][j]);
		add(t,id(i,j),0);
	}
	pcnt = n * m + 1;
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		add(s,++pcnt,sa[i][j]);
		add(pcnt,s,0);
		add(pcnt,id(i,j),0x3f3f3f3f);
		add(id(i,j),pcnt,0);
		if(i + 1 <= n){
		add(pcnt,id(i + 1,j),0x3f3f3f3f);
		add(id(i + 1,j),pcnt,0);			
		}
		if(j + 1 <= m){
		add(pcnt,id(i,j + 1),0x3f3f3f3f);
		add(id(i,j + 1),pcnt,0);			
		}
		if(i - 1 > 0){
		add(pcnt,id(i - 1,j),0x3f3f3f3f);
		add(id(i - 1,j),pcnt,0);			
		}
		if(j - 1 > 0){
		add(pcnt,id(i,j - 1),0x3f3f3f3f);
		add(id(i,j - 1),pcnt,0);				
		}									
	}
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		add(++pcnt,t,ss[i][j]);
		add(t,pcnt,0);
		add(id(i,j),pcnt,0x3f3f3f3f);
		add(pcnt,id(i,j),0);
		if(i + 1 <= n){
		add(id(i + 1,j),pcnt,0x3f3f3f3f);
		add(pcnt,id(i + 1,j),0);			
		}
		if(j + 1 <= m){
		add(id(i,j + 1),pcnt,0x3f3f3f3f);
		add(pcnt,id(i,j + 1),0);	
		}
		if(i - 1 > 0){
		add(id(i - 1,j),pcnt,0x3f3f3f3f);
		add(pcnt,id(i - 1,j),0);	
		}
		if(j - 1 > 0){
		add(id(i,j - 1),pcnt,0x3f3f3f3f);
		add(pcnt,id(i,j - 1),0);	
		}														
	}
	std::cout<<sum - dinic();
}
posted @ 2021-03-01 21:30  fhq_treap  阅读(79)  评论(0编辑  收藏  举报