inic声明:本博客默认读者会最大流最小割的定理,会Dinic,
最小割在数值上 == 最大流
<mark>但是在意义上没有任何关系,姑且可以这样求得最小割,当然可以自行百度最小割的证明定理</mark>
还是从题目开始说起
洛谷P1361
<mark>题目描述</mark>
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1…n编号)。
现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M找到了规则***有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益。
小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
<mark>输入格式</mark>
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合***有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。
<mark>输出格式</mark>
只有一行,包括一个整数,表示最大收益
<mark>输入输出样例</mark>
**输入 **
3
4 2 1
2 3 2
1
2 3 2 1 2
**输出 **
11
如果说我们不考虑额外的收益那么我们很容易建立如下的模型
这样建图后我们在跑一遍最大流求得最小割就好了,有些人不清楚为什么要求最小割这里简单的解释一下,我们建立原点和汇点分别表示两块地但是如果即链接了A又连接了B那肯定是不行的,因为一种作物只可以种植在唯一的土地上,那么也就是说我们必须要断开一条边,既然要取得最大的收益那么我们肯定要断掉最小的边,咦~~~~~~~~~这不是最小割嘛233333;
但是现在有一种额外收益的情况,那我们就要建立关于额外收益的点,也就是辅助点或者说是虚点
<mark>那么我们建立如下模型</mark>
我们建立两个虚点,v1和v2分别表示一个集合对A或者B作出贡献的收益点,这样我们跑一遍最大流求出最小割,就OKK了 啊
AC代码
/* * ┏┓ ┏┓+ + * ┏┛┻━━━━━━━┛┻┓ + + * ┃ ┃ * ┃ ━ ┃ ++ + + + * █████━█████ ┃+ * ┃ ┃ + * ┃ ┻ ┃ * ┃ ┃ + + * ┗━━┓ ┏━┛ * ┃ ┃ * ┃ ┃ + + + + * ┃ ┃ Code is far away from bug with the animal protecting * ┃ ┃ + 神兽保佑,代码无bug * ┃ ┃ * ┃ ┃ + * ┃ ┗━━━┓ + + * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━━━┳┓┏┛ + + + + * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛+ + + + */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
#include<vector>
#include<cmath>
#include<functional>
#include<stack>
using namespace std;
const int maxn = 255;
const int MAXN = 4e6 + 5;
const int INF = 0x3f3f3f3f;
struct Edge
{
int to, w, next;
}edg[MAXN];
int head[MAXN], dep[MAXN], cnt = 0;
void init()
{
memset(head, -1, sizeof(head));
cnt = 0;
}
void Add_edge(int u, int v, int w)
{
edg[cnt].w = w;
edg[cnt].to = v;
edg[cnt].next = head[u];
head[u] = cnt++;
edg[cnt].w = 0;
edg[cnt].to = u;
edg[cnt].next = head[v];
head[v] = cnt++;
}
bool Bfs(int s, int t)
{
memset(dep, 0, sizeof(dep));
queue<int> q;
while(!q.empty()) q.pop();
dep[s] = 1;
q.push(s);
while(!q.empty())
{
int p = q.front();
q.pop();
for(int i = head[p]; i != -1; i = edg[i].next)
{
int w = edg[i].w; int v = edg[i].to;
if(w && !dep[v])
{
dep[v] = dep[p] + 1;
q.push(v);
if(v == t)
return true;
}
}
}
if(dep[t])
return true;
else
return false;
}
int DFS(int s, int mw, int t)
{
if(s == t)
return mw;
int res = mw;
for(int i = head[s]; i != -1; i = edg[i].next)
{
if(!res) return mw;
int v = edg[i].to;
if(edg[i].w && dep[v] == dep[s] + 1)
{
int k = DFS(v, min(res, edg[i].w), t);
if(!k) dep[v] = 0;
edg[i].w -= k;
edg[i^1].w += k;
res -= k;
}
}
return mw - res;
}
int Dinic(int s, int t)
{
int tot = 0;
while(Bfs(s, t))
{
while(int d = DFS(s, INF, t))
tot += d;
}
return tot;
}
//最小割在数值上等于最大流
//辅助点建图法
int main()
{
int n, w, m, k, c1, c2, u;
long long sum = 0;
scanf("%d", &n);
init();
int s = n + 1, t = n + 2;
register int i;
for(i = 1; i <= n; ++i)
{
scanf("%d", &w);
sum += w;
Add_edge(s, i, w);
}
for(i = 1; i <= n; ++i)
{
scanf("%d", &w);
sum += w;
Add_edge(i, t, w);
}
scanf("%d", &m);
for(int a = 1; a <= m; ++a)
{
scanf("%d %d %d", &k, &c1, &c2);
int v1 = n + 2 + a;
int v2 = n + 2 + a + m;
sum += (c1 + c2);
Add_edge(s, v1, c1);
Add_edge(v2, t, c2);
for(int j = 1; j <= k; ++j)
{
scanf("%d", &u);
Add_edge(v1, u, INF);
Add_edge(u, v2, INF);
}
}
cout << sum - Dinic(s, t);
}
有一个 坑点就是数据范围要开到4e6才能过,,,,切记初始化