记状态 i 的面积和为 sum[i]
s
u
m
[
i
]
,则转移为 sum[i]=sum[lowbit(i)]+sum[i−lowbit(i)]
s
u
m
[
i
]
=
s
u
m
[
l
o
w
b
i
t
(
i
)
]
+
s
u
m
[
i
−
l
o
w
b
i
t
(
i
)
]
(其实任选两个 i
i
的子集相加即可,这样写只是方便)。
记状态 i 最多能选取的子集个数为 f[i]
f
[
i
]
,则转移为 f[i]=maxj=1n1+n2{f[i−2j−1]}+(sum[i]==0)
f
[
i
]
=
max
j
=
1
n
1
+
n
2
{
f
[
i
−
2
j
−
1
]
}
+
(
s
u
m
[
i
]
==
0
)
(这里并不需要枚举子集转移,因为 i
i
的子集肯定被 i−2j−1 的子集包含,之前已经转移过了)。
时间复杂度 O(2n1+n2×(n1+n2))
O
(
2
n
1
+
n
2
×
(
n
1
+
n
2
)
)
。
Code
#include <iostream>#include <cstdio>#include <cctype>#include <algorithm>#include <cstring>#include <cstdlib>usingnamespacestd;
inlineint get()
{
char ch; int res = 0; bool flag = false;
while (ch = getchar(), !isdigit(ch) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
inlinevoid CkMax(int &x, int y)
{
if (x < y) x = y;
}
constint N = 11e5 + 5;
int n, m, sum[N], f[N];
int main()
{
n = get();
for (int i = 0; i < n; ++i) sum[1 << i] = get();
m = get();
for (int i = 0; i < m; ++i) sum[1 << n + i - 1] = -get();
n += m;
constint C = 1 << n;
for (int i = 1; i < C; ++i)
{
int tmp = i & -i;
sum[i] = sum[tmp] + sum[i ^ tmp];
for (int j = 0; j < n; ++j)
if (1 << j & i) CkMax(f[i], f[1 << j ^ i]);
if (!sum[i]) ++f[i];
}
printf("%d\n", n - (f[C - 1] << 1));
}