Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

一年半前为了省选学网络流之后第一次做网络流的题

首先是自己改造后的Dinic:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#define INF 2147483647
#define MAXN 10005
#define MAXM 100005
using namespace std;
struct addedge
{
	int to,flow,cap,ne;
}e[MAXM << 1];
int head[MAXN];
int cur[MAXN];
int dep[MAXN];
int ecnt = -1;
int N,M,S,T;
void init()
{
	memset(head,-1,sizeof head);
	memset(dep,0,sizeof dep);
	return ;
}
void addedge(int u,int v,int cap)
{
	e[++ecnt].to = v;
	e[ecnt].flow = 0;
	e[ecnt].cap = cap;
	e[ecnt].ne = head[u];
	head[u] = ecnt;
	return ;
}
queue<int> q;
int bfs()
{
	memset(dep,0,sizeof dep);
	dep[S] = 1;
	q.push(S);
	while(!q.empty())
	{
		int nown = q.front();
		q.pop();
		for(int i = head[nown];i != -1;i = e[i].ne)
		{
			int to = e[i].to;
			if(dep[to] != 0 || e[i].flow >= e[i].cap) continue;
			dep[to] = dep[nown] + 1;
			q.push(to);
		}
	}
	return dep[T];
}
int homin(int a,int b)
{
	if(a < b) return a;
	return b;
}
int dfs(int x,int fl)
{
	if(x == T) return fl;
	for(int &i = cur[x]; i != -1;i = e[i].ne)
	{
		int to = e[i].to;
		if(dep[to] != dep[x] + 1 || e[i].flow >= e[i].cap) continue;
		int f = dfs(to,homin(fl,e[i].cap - e[i].flow));
		if(f > 0)
		{
			e[i].flow += f;
			e[i^1].flow -= f;
			return f;
		}
	}
	return 0;
}
int Dinic()
{
	int ans = 0;
	while(bfs())
	{
		memcpy(cur,head,sizeof cur);
		while(int d = dfs(S,INF)) ans += d;
	}
	return ans;
}
void read(int &ss)
{
	ss=0;char ch=getchar();while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') ss*=10,ss+=ch-'0',ch=getchar();return ;
}
int main()
{
	init();
	cin >> N >> M >> S >> T;
	for(int i = 0;i < M;i++)
	{
		int tu,tv,tw;
		cin >> tu >> tv >> tw;
		addedge(tu,tv,tw);
		addedge(tv,tu,0);
	}
	cout << Dinic() << endl;
}

就用网络流24题当例题了

最大流

P2766 最长不下降子序列问题

第一问直接n^2就行,dp后的数组记为f,其中f[i]代表以第i个数结尾的最长不下降子序列的长度,答案记为ans1. a[i]代表第i个数.

第二问可以给对于每个 ,如果 并且 ,就意味着选完第j个数后,再选第i个数,可以选到一个最长的不下降子序列.

因为最后是要记录最长不下降子序列的个数,所以每个最长不下降子序列对答案的贡献均为1,所以可以从j到i连一条容量为1的边. 因为所有 的点a都可作为起点,所有 的点b都可作为终点,所以可以用超级源向所有满足条件的点a连一条边,所有满足条件的点b向超级汇连一条边.同理,这些边容量均为1.

这样对整个图跑一个最大流即可得到答案.但是为了保证每个数只被用一次,需要利用拆点限制每个点的流量为1.即将一个点拆为两个点,并且这两个点之间仅有一条容量为1的边.

第三问只是取消了 的使用次数限制,所以只要把限制这两个点使用次数的边的容量全部改为INF即可,即超级源向 的边, 到超级汇的边, 分别拆成的两点之间的边这四条边的容量限制改为INF后再跑最大流即可.

超级汇有一种整合所有信息的感觉,将所有和答案直接相关的节点流量信息全部汇总起来.

对于点流量的限定,一般用拆点的方法.拆点可以用*2-1和*2防止撞车.

图可以表示元素之间的关系,这题元素之间的关系十分明显(走过一个节点下一个可以走哪些节点),考虑建图.

第二问是一种"并列"的问题,取出的几个子序列地位相同,且均满足相同的条件.网络流解决的也是这种"并列"的问题,在图中,每一个节点,每一条路径都是地位相同的.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#define INF 2147483647
#define MAXN 550
#define osu(a,b,i) for(int i = (a);i <= (b);i++)
#define nso(a,b,i) for(int i = (a);i >= (b);i--)
#define lovelive long long int
using namespace std;
inline int homin(int a,int b)
{
	if(a > b) return b;
	return a;
}
struct Edge{
	int to,cap,flow,ne;
};
Edge e[MAXN << 2];
Edge e2[MAXN << 2];
int dep[MAXN << 1];
int cur[MAXN << 1];
int head[MAXN << 1];
int ecnt = -1;
int S,T;
int N;
int a[MAXN];
queue <int> q;
void init()
{
	memset(e,0,sizeof e);
	memset(head,-1,sizeof head);
	memset(dep,0,sizeof dep);
}
int addedge(int u,int v)
{
	e[++ecnt].to = v;
	e[ecnt].cap = 1;
	e[ecnt].flow = 0;
	e[ecnt].ne = head[u];
	head[u] = ecnt;
	e[++ecnt].to = u;
	e[ecnt].cap = 0;
	e[ecnt].flow = 0;
	e[ecnt].ne = head[v];
	head[v] = ecnt;
	return ecnt - 1;
}
int bfs()
{
	queue<int> q2;
	swap(q,q2);
	memset(dep,0,sizeof dep);

	q.push(S);
	dep[S] = 1;
	//cout << S << "dd" <<q.front()<< endl;
	while(!q.empty())
	{
		int nown = q.front();
		//cout << nown << "}" << endl;
		q.pop();
		for(int i = head[nown];i!=-1;i = e[i].ne)
		{
			//cout << e[i].to << "gs" << dep[e[i].to]<<endl;
			int to = e[i].to;
			if(dep[to] != 0 || e[i].flow >= e[i].cap) continue;
			//cout << "!" << endl;
			dep[to] = dep[nown] + 1;
			q.push(to);
		}
	}
	return dep[T];
}
int dfs(int x,int fl)
{
	if(x == T) return fl;
	for(int &i = cur[x];i != -1;i = e[i].ne)
	{
		int to = e[i].to;
		if(dep[to] != dep[x]+1 || e[i].flow >= e[i].cap) continue;
		int f = dfs(to,homin(fl,e[i].cap - e[i].flow));
		if(f > 0)
		{
			e[i].flow += f;
			e[i^1].flow -= f;
			return f;
		}
	}
	return 0;
}
int Dinic()
{
	int flow = 0;
	//cout << "!" << endl;
	while(bfs())
	{
		//cout << "!" << endl;
		memcpy(cur,head,sizeof cur);
		while(int d = dfs(S,INF)) flow += d;
		//cout << "?" << endl;
	}
	return flow;
}
int f[MAXN];
int fdp()
{
	int ans = -1;
	for(int i = 1;i <= N;i++)
	{
		int maxx = 0;
		for(int j = 1;j < i;j++)
		{
			if(f[j] > maxx && a[j] <= a[i])
			{
				maxx = f[j];
			}
		}
		f[i] = maxx + 1;
		if(f[i] > ans) ans = f[i];
	}
	return ans;
}
int main()
{
	init();
	cin >> N;
	for(int i = 1;i <= N;i++)
	{
		cin >> a[i];
	}
	int ans1 = fdp();
	S = 0;T = 2*N+1;
	int ea,eb,ec,ed;
	for(int i = 1;i <= N;i++)
	{
		if(i == 1)
		{
			ec = addedge(2*i-1,2*i);
		}else if(i == N)
		{
			ed = addedge(2*i-1,2*i);
		}else{
			addedge(2*i-1,2*i);
		}
		if(f[i] == 1)
		{
			if(i == 1)
			{
				ea = addedge(S,2*i-1);
			}else{
				addedge(S,2*i-1);
			}
		}
		if(f[i] == ans1)
		{
			if(i == N)
			{
				eb = addedge(2*i,T);
			}else{
				addedge(2*i,T);
			}
		}
		for(int j = 1;j < i;j++)
		{
			if(f[i] == f[j] + 1 && a[i] >= a[j])
			{
				addedge(2*j,2*i-1);
			}
		}
	}
	memcpy(e2,e,sizeof e);
	cout << ans1 << endl;
	cout << Dinic() << endl;
	cout << ea << " " << eb << " " << ec << " " << ed << endl;
	//cout << ":1" << endl;
	memcpy(e,e2,sizeof e);
	//cout << ":2" << endl;
	if(ea >= 0 && ea <= ecnt) e[ea].cap = INF;
	if(eb >= 0 && eb <= ecnt) e[eb].cap = INF;
	if(ec >= 0 && ec <= ecnt) e[ec].cap = INF;
	if(ed >= 0 && ed <= ecnt) e[ed].cap = INF;
	//cout << ":3" << endl;
	if(N == 1)
	{
		cout << 1 << endl;
	}
	else{
		cout << Dinic() << endl;
	}
}

最小割

P2774 方格取数问题

这道题也是一中"并列"的问题,要所有元素地位均相同,需要从中挑选出几个.

"割"的概念是,让这个图的原点无法到达汇点,意味着关系的断绝.

将这个方格棋盘染色后,就是让黑点和其相邻的白点不能同时出现.

只要让建立的图反映出,当黑点和相邻的白点同时出现时,源点和汇点就会联通,这样让图不连通就意味着没有黑点和相邻白点同时出现.

为了让删去的点值最小,要把点值反映到边权上,才能利用"最小"割求解

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#define INF 2147483647
#define MAXN 10005
#define MAXM 100005
using namespace std;
struct addedge
{
	int to,flow,cap,ne;
}e[MAXM << 1];
int head[MAXN];
int cur[MAXN];
int dep[MAXN];
int ecnt = -1;
int N,M,S,T;
void init()
{
	memset(head,-1,sizeof head);
	memset(dep,0,sizeof dep);
	return ;
}
void addedge(int u,int v,int cap)
{
	e[++ecnt].to = v;
	e[ecnt].flow = 0;
	e[ecnt].cap = cap;
	e[ecnt].ne = head[u];
	head[u] = ecnt;
	return ;
}
queue<int> q;
int bfs()
{
	memset(dep,0,sizeof dep);
	dep[S] = 1;
	q.push(S);
	while(!q.empty())
	{
		int nown = q.front();
		q.pop();
		for(int i = head[nown];i != -1;i = e[i].ne)
		{
			int to = e[i].to;
			if(dep[to] != 0 || e[i].flow >= e[i].cap) continue;
			dep[to] = dep[nown] + 1;
			q.push(to);
		}
	}
	return dep[T];
}
int homin(int a,int b)
{
	if(a < b) return a;
	return b;
}
int dfs(int x,int fl)
{
	if(x == T) return fl;
	for(int &i = cur[x]; i != -1;i = e[i].ne)
	{
		int to = e[i].to;
		if(dep[to] != dep[x] + 1 || e[i].flow >= e[i].cap) continue;
		int f = dfs(to,homin(fl,e[i].cap - e[i].flow));
		if(f > 0)
		{
			e[i].flow += f;
			e[i^1].flow -= f;
			return f;
		}
	}
	return 0;
}
int Dinic()
{
	int ans = 0;
	while(bfs())
	{
		memcpy(cur,head,sizeof cur);
		while(int d = dfs(S,INF)) ans += d;
	}
	return ans;
}
int a[105][105];
int main()
{
	init();
	cin >> M >> N;
	S = N*M;
	T = N*M+1;
	int sum = 0;
	for(int i = 1;i <= M;i++)
	{
		for(int j = 1;j <= N;j++)
		{
			cin >> a[i][j];
			sum += a[i][j];
		}
	}
	for(int i = 1;i <= M;i++)
	{
		for(int j = 1;j <= N;j++)
		{
			int num = (i-1)*N + j-1;
			if((i+j) % 2 == 1)
			{
				addedge(S,num,a[i][j]);
				addedge(num,S,0);
				if(i > 1)
				{
					addedge(num,num-N,INF);
					addedge(num-N,num,0);
				}
				if(i < M)
				{
					addedge(num,num+N,INF);
					addedge(num+N,num,0);
				}
				if(j < N)
				{
					addedge(num,num+1,INF);
					addedge(num+1,num,0);
				}
				if(j > 1)
				{
					addedge(num,num-1,INF);
					addedge(num-1,num,0);
				}
			}else{
				addedge(num,T,a[i][j]);
				addedge(T,num,0);
			}
		}
	}
	cout << sum - Dinic() << endl;
	return 0;
}

To be continued...

评论