zoukankan      html  css  js  c++  java
  • 【网络流杂谈】

    简介:

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

    题目:

    一:[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();
    }
    
  • 相关阅读:
    QT移植详解
    如何构建Qt4.7.0嵌入式开发环境
    Qt触摸屏、键盘的驱动[转]
    【详解】如何编写Linux下Nand Flash驱动
    QT安装 -->Error: register or shift expression expected -- `orr r3,r2,lsl#16'
    QT环境搭建常见的bug解决 -->ld: cannot find –lbootstrap 错误
    SDK支付流程
    IOS与Unity交互
    Android
    [转]个人对AutoResetEvent和ManualResetEvent的理解
  • 原文地址:https://www.cnblogs.com/dixiao/p/14466024.html
Copyright © 2011-2022 走看看