[洛谷 P1361] 小M的作物 (最小割 好题)

[洛谷 P1361] 小M的作物


题目链接


大致题意:

懒癌

题是好题,题解不算是好题解


解题思路:

对于每一株作物,仅能种在 A/B 的一块田地里,也就是二选一

如果只有一个作物,我们肯定选择种在A或者种在B两者中最大的一条边,删掉小边

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SnAcnWb-1637323198129)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211119195358373.png)]

那多个作物呢,同理,去掉最小边,使其分成两个不相通的集合,到这里,不就是求最小割吗?

但是较为麻烦的是额外的收益怎么处理呢

对于这种情况,我们将这个组合里所有点作为一个点集,根据AB区,分别连向s,t,建立一个新点x

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vAYuY18D-1637323198132)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211119195401982.png)]

用s连向x,即

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZHXbZEL-1637323198134)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211119195528387.png)]

同样,建立一个新点y,连向t

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jkQiBo3o-1637323198139)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211119195918858.png)]

为了保证割边时,只会割s->x或者y->t的边,把x,y连向点集的边设为INF

求最大的收益,即总收益-最小割


AC代码:

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1e6 + 10, M = 2e6 + 10, INF = 1e9 + 7;
int n, m, s, t;
int h[N], e[M], ne[M], f[M], idx;
int dep[N], cur[N];
void add(int a, int b, int c) {
	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}
bool bfs() {
	memset(dep, -1, sizeof dep);
	queue<int>q;
	q.push(s); dep[s] = 0; cur[s] = h[s];
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = h[u]; ~i; i = ne[i]) {
			int v = e[i];
			if (dep[v] == -1 && f[i]) {
				dep[v] = dep[u] + 1;
				cur[v] = h[v];
				if (v == t)return true;
				q.push(v);
			}
		}
	}
	return false;
}
int find(int u, int limit) {
	if (u == t)return limit;
	int flow = 0;
	for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
		cur[u] = i;
		int v = e[i];
		if (dep[v] == dep[u] + 1 && f[i]) {
			int tmp = find(v, min(f[i], limit - flow));
			if (!tmp)dep[v] = -1;
			f[i] -= tmp; f[i ^ 1] += tmp; flow += tmp;
		}
	}
	return flow;
}
int dinic() {
	int res = 0, flow;
	while (bfs())
		while (flow = find(s, INF))
			res += flow;
	return res;
}
int main(void)
{
	memset(h, -1, sizeof h);
	cin >> n; s = 0, t = n + 1;
	int sum = 0;
	for (int i = 1; i <= n; ++i) {
		int x; cin >> x; sum += x;
		add(s, i, x); add(i, s, 0);
	}
	for (int i = 1; i <= n; ++i) {
		int x; cin >> x; sum += x;
		add(i, t, x); add(t, i, 0);
	}
	cin >> m;
	for (int i = 1; i <= m; ++i) {
		int w1, w2, cnt; cin >> cnt >> w1 >> w2;
		sum += w1 + w2;
		add(s, n + 10 + 2 * i - 1, w1);
		add(n + 10 + 2 * i, t, w2);
		while (cnt--) {
			int x; cin >> x;
			add(n + 10 + 2 * i - 1, x, INF);
			add(x, n + 10 + 2 * i, INF);
		}
	}
	int res = sum - dinic();
	cout << res << endl;
	return 0;
}
  • i - 1, x, INF);
    add(x, n + 10 + 2 * i, INF);
    }
    }
    int res = sum - dinic();
    cout << res << endl;
    return 0;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值