网络流与线性规划24题

留坑待填


网络流做题技巧:

1、板子很熟的情况下肉眼差错,放弃单步调试。

2、反复检查自己的网络流模型的正确性,确保正确才开始写。

3、手动构造多组数据卡自己的程序,网络流的题目通常样例很垃圾而且对拍不好写。


通用模板

最大流模板:

#include 
#include 
#include 
#include 
#include 

#define N 10050
#define INF 1<<30

using namespace std;

struct Node{int u,cap,rec; };
vector e[N];
int p[N],S,T;
void add_Node(int a,int b,int v) {
	Node tmp;
	tmp.u = b;
	tmp.cap = v;
	tmp.rec = e[b].size();
	e[a].push_back(tmp);
	
	tmp.u = a;
	tmp.cap = 0;
	tmp.rec = e[a].size() - 1;
	e[b].push_back(tmp);
	return ;
}

bool BFS() {
	bool flag = false;
	memset(p,0,sizeof(p));
	queue q;
	p[S] = 1;
	while (!q.empty()) {
		int u = q.front(); q.pop();
		if (u == T) flag = true;
		for (int i=0;i 0 && p[v] == 0) {
				p[v] = p[u] + 1;
				q.push(v);
			}
		}
	}
	return flag;
}

int DFS(int u,int flow) {
	if (u == T) return flow;
	int f = flow , g = 0;
	for (int i=0;i 0 && p[v] == p[u] + 1 && (tmp = DFS(v,min(f,cp))) > 0) {
			f -= tmp;
			g += tmp;
			e[u][i].cap -= tmp;
			int rc = e[u][i].rec;
			e[v][rc].cap += tmp;
		}
	}
	return g;
}

void build() {
	
}

int main()
{
	build();
	int max_flow = 0;
	while (BFS()) max_flow += DFS(S,INF);
	int ans = max_flow;
	printf("%d\n",ans);
	return 0;
}



24题建图

第一题:飞行员配对方案(二分图最大匹配)

建图:直接二分图建图,S连向所有皇家飞行员流量为INF,所有外国飞行员连向T流量为INF,每个皇家飞行员连向外国飞行员流量为1。最大流,最后输出所有的满流边的

void build() {
	while (1)
	{
		scanf("%d%d",&x,&y);
		if (x == y && y == -1) break;
		if (x>y) swap(x,y);
		add_Node(x,y,1);
	}
}


第二题:太空飞行计划问题(最大权闭合子图)

建图:超级源连向所有正权点,流量为权值,负权点连向超级汇,流量为权值(取绝对值),对所有的正权和负权的限制,从正权点连向负权点一条边,流量为INF(这条边不会被剪掉)。跑最小割,答案 = 正权点权值和 - 最小割。

思路:剪掉一个正权点即为放弃一个收益,剪掉一个负权点即为放弃一个限制。

void build() {
	scanf("%d%d",&m,&n);
	S = ++cnt; T = ++cnt;
	for (int i=1;i<=m;i++) l[i] = ++cnt;
	for (int i=1;i<=n;i++) r[i] = ++cnt;
	for (int i=1;i<=m;i++) {
		scanf("%d",&v);
		ans += v;
		add_Node(S,l[i],v);
		char ch = ' ';
		while (ch != '\n') {
			scanf("%d",&t); ch = getchar();
			add_Node(l[i],r[t],INF);
		}
	}
	for (int i=1;i<=n;i++) {
		scanf("%d",&v); add_Node(r[i],T,v);
	}
}


第三题:最小路径覆盖问题(有向无环图最小路径覆盖)

建图:将所有的点拆成两个点,S连向所有的xi容量为1,所有的yi连向T容量为1,对于原图中的每一条有向边E(u,v),在建图中加入边,容量为1。答案等于点数-最大流。

思路:xi相当于一个点的出度点,yi相当于一个点的入度点。在最小路径覆盖问题中,一个点的出入度只能为0/1,所以它们连向超级源和超级汇的容量都为1。没有一条连向x,y的漫流边即为在路径覆盖中x,y被连接了。


void build() {
	S = ++cnt; T = ++cnt;
	for (int i=1;i<=n;i++) x[i] = ++cnt;
	for (int i=1;i<=n;i++) add_Node(S,x[i],1);
	
	for (int i=1;i<=n;i++) y[i] = ++cnt;
	for (int i=1;i<=n;i++) add_Node(y[i],T,1);
	
	for (int i=1;i<=m;i++) {
		int u = x[ e[i].a ]  , v = y[ e[i].y ];
		add_Node(u,v,1);
	}
}


第四题:魔术球问题(有向无环图最小路径覆盖)

建图:二分最大值,将所有的球拆点成xi,yi,分别连向超级源和超级汇,容量为1,对于球a,b(a容量为1,若点数-最大流<=柱子数目,则判定为true.

思路:最小路径覆盖问题

void build() {
	S = ++cnt; T = ++cnt;
	for (int i=1;i<=n;i++) x[i] = ++cnt;
	for (int i=1;i<=n;i++) add_Node(S,x[i],1);
	
	for (int i=1;i<=n;i++) y[i] = ++cnt;
	for (int i=1;i<=n;i++) add_Node(y[i],T,1);
	
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (floor( sqrt(i+j) ) == sqrt(i+j)) add_Node(i,j,1);
}

第五题:圆桌问题(二分图多重匹配)

建图:

S连向所有的单位,容量为该单位人数。

单位连向所有的餐桌,容量为1。

所有的餐桌连向T,容量为餐桌的容量。

最大流。输出所有满流的<单位、餐桌>边。

void build() {
	scanf("%d%d",&n,&m);
	S = ++cnt; T = ++cnt;
	for (int i=1;i<=n;i++) {
		scanf("%d",&t);
		l[i] = ++cnt;
		add_Node(S,l[i],t);
	}
	
	for (int i=1;i<=m;i++) {
		scanf("%d",&t);
		r[i] = ++cnt;
		add_Node(r[i],T,t);
	}
	
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) add_Node(l[i],r[j],1);
}

第六题:最长递增子序列问题(最大不相交路径)

诶一开始建的图是错的,少考虑了很多东西

第一问第二问DP,第三问网络流(需要借助前两问的dp数组)

令F[i]为最长的以i结尾的LIS的长度

建图:

对所有的点拆成xi,yi,且加入边xi,yi,如果是两端的点则容量为INF否则容量为1

对所有的F[i] = 1,S连向xi容量为1

对所有的F[i] = 第一问答案,yi连向T容量为1

对于每一对$ $满足$ F[i]+1 = F[j] $ , $i容量为1。

最大流。

void build() {
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (F[i]+1 == F[j]) add_Node(i,j,1);
}



1-6题完成时间:2016年12月10日-2016年12月13日




你可能感兴趣的:(网络流与线性规划24题)