“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛——G.养花【网络流】

题目传送门


在这里插入图片描述


题解

  • 直接建图跑网络流即可。
  • 对于区间的最大流,可以使用线段树,也可以直接加边。
  • 注意数据范围不要太小。(奇奇怪怪,开小一点就超时???什么毛病)

AC-Code

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int inf = 0x3f3f3f3f;
const int maxn = 1e6 + 7;

struct Edge {
	int to;
	int next;
	int val;
};
struct Dinic {
	Edge edge[maxn << 1];	// 双向边,开 2 倍数组
	int head[maxn];
	int cnt; // 边的数量,从 0 开始编号
	int depth[maxn]; // 分层图标记深度

	void init() {
		memset(head, -1, sizeof(head));
		cnt = 0;
	}

	void add(int u, int v, int w) {
		edge[cnt].to = v;
		edge[cnt].next = head[u];
		edge[cnt].val = w;
		head[u] = cnt++;
	}

	// bfs分层图
	bool bfs(int s, int t) {
		queue<int>q;
		memset(depth, 0, sizeof depth);
		depth[s] = 1; // 源点深度为 1
		q.push(s);
		while (!q.empty()) {
			int u = q.front();
			q.pop();
			for (int i = head[u]; ~i; i = edge[i].next) {
				int v = edge[i].to;
				if (edge[i].val > 0 && depth[v] == 0) { // 如果残量不为0,且 v 还未分配深度
					depth[v] = depth[u] + 1;
					q.push(v);
				}
			}
		}
		return depth[t]; // 汇点深度为 0:不存在分层图,返回false;
						 //           非 0 :存在增广路,返回true
	}

	// dfs寻找增广路
	int dfs(int u, int flow, int t) {
		if (u == t || flow <= 0) // 到达汇点
			return flow;
		int rest = flow;
		for (int i = head[u]; ~i; i = edge[i].next) {
			int v = edge[i].to;
			if (depth[v] == depth[u] + 1 && edge[i].val != 0) { // 满足分层图、残量>0 两个条件
				int k = dfs(v, min(rest, edge[i].val), t); // 向下增广
				rest -= k;
				edge[i].val -= k; // 正向边减
				edge[i ^ 1].val += k; // 反向边加
			}
		}
		return flow - rest; // flow:推送量,rest:淤积量,flow - rest:接受量/成功传递量
	}

	int maxflow(int s, int t) {
		int ans = 0;
		while (bfs(s, t)) {
			while (int d = dfs(s, inf, t))
				ans += d;
		}
		return ans;
	}
}DC;

int sum[maxn];	// sum[i]:高度为i的树的数量
int main() {
	ios;
	int t;	cin >> t;
	while (t--) {
		DC.init();
		int n, m, k;	cin >> n >> m >> k;
		for (int i = 0; i <= k; ++i)	sum[i] = 0;
		for (int i = 0; i < n; ++i) {
			int x;	 cin >> x;
			++sum[x];
		}
		int num = k;	// 拓展结点,先++再使用,从k+1起始
		for (int i = 1; i <= m; ++i) {
			int op, c;	cin >> op >> c;
			if (op == 1) {
				int a0, b0;	cin >> a0 >> b0;
				DC.add(a0, b0, c);	DC.add(b0, a0, 0);
			}
			else if (op == 2) {
				int a1, a2, b1;	cin >> a1 >> a2 >> b1;
				++num;
				for (int j = a1; j <= a2; ++j) {
					DC.add(j, num, c);	DC.add(num, j, 0);
				}
				DC.add(num, b1, c);	DC.add(b1, num, 0);
			}
			else if (op == 3) {
				int a1, b1, b2;	cin >> a1 >> b1 >> b2;
				++num;
				DC.add(a1, num, c);	DC.add(num, a1, 0);
				for (int j = b1; j <= b2; ++j) {
					DC.add(num, j, c);	DC.add(j, num, 0);
				}
			}
			else if (op == 4) {
				int a1, a2, b1, b2;	cin >> a1 >> a2 >> b1 >> b2;
				++num;
				for (int j = a1; j <= a2; ++j) {
					DC.add(j, num, c);	DC.add(num, j, 0);
				}
				DC.add(num, num + 1, c);	DC.add(num + 1, num, 0);
				++num;
				for (int j = b1; j <= b2; ++j) {
					DC.add(num, j, c);	DC.add(j, num, 0);
				}
			}
		}
		int start = 0, end = ++num;
		for (int i = 1; i <= k; ++i) {
			if (sum[i]) {
				DC.add(start, i, sum[i]);	DC.add(i, start, 0);
			}
		}
		DC.add(k, end, n);	DC.add(end, k, 0);
		cout << DC.maxflow(start, end) << endl;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值