题目描述
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号)。
现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M找到了规则中共有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益。
小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
输入格式
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合中共有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。
输出格式
只有一行,包括一个整数,表示最大收益
输入输出样例
输入 #1
3 4 2 1 2 3 2 1 2 3 2 1 2
输出 #1
11
说明/提示
样例解释
A耕地种1,2,B耕地种3,收益4+2+3+2=11。
数据范围与约定
1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
思路
如果把农作物作为中间点,农场 A 设为源, B 设为汇。
因为对于每个农作物只能种在一个农场中,所以该题可以等效为一个最小割模型。
所以如何对 bonus 的组合建边是关键。
把整个组合点集看作是一个点并拆成出入两点,由A有一条到入点的路,且从出点有一条到B的路,再逐次将待加入的点添加进点集就可以了。
这样能获得的最大值就是总价值 - 最小割
CODE
#include
<
cstdio
>
#include
<
iostream
>
#include
<
cstring
>
#include
<
queue
>
#include
<
vector
>
#include
<
algorithm
>
using
namespace std
;
const
int maxn
=
1
e
6
+
7
;
const
int inf
=
0x
3f3f3f3f
;
template
<
class T
>
inline
void
read(T
&
res
)
{
char c
;T flag
=
1
;
while((c
=
getchar())
<
'
0
'
||c
>
'
9
'
)if(c
==
'
-
'
)flag
=-
1
;res
=c
-
'
0
'
;
while((c
=
getchar())
>=
'
0
'
&&c
<=
'
9
'
)res
=res
*
10
+c
-
'
0
'
;res
*=flag
;
}
struct edge
{
int from
,to
,cap
,flow
;};
struct isap
{
int n
,s
,t
,
p
[maxn
],
d
[maxn
],
cur
[maxn
],
num
[maxn
];
bool
vis
[maxn
];
vector
<int>
g
[maxn
];
vector
<edge
>edges
;
void
init(
int
n
,
int
s
,
int
t
)
{
this
->
n
= n
;
this
->
s
= s
;
this
->
t
= t
;
for(
int i
=
1
;i
<= n
;i
++
)
g
[i
].clear();
edges
.clear();
}
void
addegde(
int
from
,
int
to
,
int
cap
)
{
edges
.push_back((edge
){from
, to
, cap
,
0}
);
edges
.push_back((edge
){to
, from
,
0
,
0}
);
int m
=
edges
.size();
g
[from
].push_back(m
-
2
);
g
[to
].push_back(m
-
1
);
}
int
augment()
{
///找增广路
int x
= t
,a
= inf
;
while(x
!=s
)
{
a
=
min(a
,
edges
[
p
[x
]].
cap
-
edges
[
p
[x
]].
flow
);
x
=
edges
[
p
[x
]].
from
;
}
x
=t
;
while(x
!= s
)
{
edges
[
p
[x
]].
flow
+= a
;
edges
[
p
[x
]
^
1
].
flow
=
-a
;
x
=
edges
[
p
[x
]].
from
;
}
return a
;
}
int
maxflow()
{
///更新最大流
int flow
=
0
;
memset(num
,
0
,
sizeof
(num
));
memset(cur
,
0
,
sizeof
(cur
));
for(
int i
=
1
; i
<= n
; i
++
)
num
[
d
[i
]]
++
;
int x
= s
;
while(
d
[s
]
< n
)
{
///最长的一条链上,最大的下标是nv-1,如果大于等于nv说明已断层
if(x
== t
)
{
flow
+=
augment();
x
= s
;
//回退
}
bool ok
=
0
;
for(
int i
=
cur
[x
]; i
<
g
[x
].size(); i
++
)
{
edge
&e
=
edges
[
g
[x
][i
]];
if(
d
[x
]
==
d
[
e
.
to
]
+
1
&&
e
.
cap
>
e
.
flow
)
{
p
[
e
.
to
]
=
g
[x
][i
];
cur
[x
]
= i
;x
=
e
.
to
;
ok
=
1
;
break;
}
}
if(
!ok
)
{
int m
= n
-
1
;
for(
int i
=
0
; i
<
g
[x
].size();i
++
)
{
edge
&e
=
edges
[
g
[x
][i
]];
if(
e
.
cap
>
e
.
flow
) m
=
min(m
,
d
[
e
.
to
]);
}
num
[
d
[x
]]
--
;
if(
!
num
[
d
[x
]])
break;
d
[x
]
= m
+
1
;
num
[
d
[x
]]
++
;
cur
[x
]
=
0
;
if(x
!= s
) x
=
edges
[
p
[x
]].
from
;
}
}
return flow
;
}
}ISAP
;
int n
, p
, q
;
int s
, t
;
int tot
=
0
;
int m
;
int
main()
{
freopen(
"
data.txt
"
,
"
r
"
, stdin
);
read(n
);
s
= n
+
1
, t
= s
+
1
;
ISAP
.init(n
* n
, s
, t
);
for
(
int i
=
1
; i
<= n
;
++i
)
{
int x
;
read(x
);
tot
+= x
;
ISAP
.addegde(s
, i
, x
);
}
for
(
int i
=
1
; i
<= n
;
++i
)
{
int x
;
read(x
);
tot
+= x
;
ISAP
.addegde(i
, t
, x
);
}
read(m
);
for
(
int i
=
1
; i
<= m
;
++i
)
{
int k
;
read(k
);
int suma
=
0
, sumb
=
0
;
read(suma
);
read(sumb
);
tot
+= suma
+ sumb
;
ISAP
.addegde(s
, n
+ i
+
2
, suma
);
ISAP
.addegde(n
+ i
+ m
+
2
, t
, sumb
);
for
(
int j
=
1
; j
<= k
;
++j
)
{
int x
;
read(x
);
ISAP
.addegde(n
+
2
+ i
, x
, inf
);
ISAP
.addegde(x
, n
+ i
+ m
+
2
, inf
);
}
}
//cout << tot << " ! " << ISAP.maxflow() << endl;
int mincut
=
ISAP
.maxflow();
printf(
"
%d
\n
"
,tot
- mincut
);
return
0
;
}