题目
题意:给定
n
(
n
<
=
18
)
n(n<=18)
n(n<=18)头奶牛,对应有
n
n
n种小麦。每个奶牛对于小麦有自己特定的喜好排列。奶牛有两种H,或G。一开始,奶牛
i
i
i拥有小麦
i
i
i。
现在对小麦重新分配,要求
- 分配后,每只奶牛得到的小麦,喜好值不能下降。
- 同时,相同类型的奶牛,才能进行小麦的重新分配,即分配前的小麦所属奶牛,和分配后的小麦所属奶牛,应该要属于同一种类型。
有 Q ( Q < = 2 N ) Q(Q<=2^N) Q(Q<=2N)次查询,每次查询给出每只奶牛的类型,求对应场景下的,分配方案数有多少种。
官方题解
思路:定义
d
p
[
m
a
s
k
]
[
l
a
s
t
]
dp[mask][last]
dp[mask][last](其中mask最高位为
i
i
i)表示除了小麦
i
i
i和奶牛
l
a
s
t
last
last,小麦集
m
a
s
k
⨁
i
mask\bigoplus i
mask⨁i已经得到配对,奶牛集
m
a
s
k
⨁
l
a
s
t
mask\bigoplus last
mask⨁last已经得到配对,的方案数。
得到匹配是指,匹配的小麦(奶牛)集中任意一个小麦(奶牛),可以在奶牛(小麦)集中找到能吃它(被吃)的。
dp转移思路,对于当前的 d p [ m a s k ] [ l a s t ] dp[mask][last] dp[mask][last],
如果奶牛
l
a
s
t
last
last中,存在可以吃的小麦
k
(
k
<
i
)
k(k<i)
k(k<i)(即小麦
k
k
k的喜好值大于等于奶牛
l
a
s
t
last
last原先吃的小麦),且
k
k
k还没有在小麦集
l
a
s
t
⨁
i
last\bigoplus i
last⨁i中。那么奶牛
l
a
s
t
last
last可以选择吃掉小麦。那么此时没配对的奶牛,则转移为奶牛
k
k
k,掩码转移为
m
a
s
k
(
1
<
<
k
)
mask^(1<<k)
mask(1<<k)
此时
d
p
[
m
a
s
k
t
⨁
(
1
<
<
k
)
]
[
k
]
+
=
d
p
[
m
a
s
k
]
[
l
a
s
t
]
dp[maskt\bigoplus (1<<k)][k]+=dp[mask][last]
dp[maskt⨁(1<<k)][k]+=dp[mask][last]。
如果奶牛
l
a
s
t
last
last可以和小麦
i
i
i匹配,那么此时
m
a
s
k
mask
mask则闭环了,即小麦集
m
a
s
k
mask
mask和奶牛集
m
a
s
k
mask
mask都得到了匹配了
此时
a
n
s
[
m
a
s
k
]
+
=
d
p
[
m
a
s
k
]
[
l
a
s
t
]
ans[mask]+=dp[mask][last]
ans[mask]+=dp[mask][last]
dp和ans初始化: d p [ 1 < < k ] [ k ] = 1 , a n s [ 0 ] = 1 dp[1<<k][k]=1,ans[0]=1 dp[1<<k][k]=1,ans[0]=1
求完每个
a
n
s
[
m
a
s
k
]
ans[mask]
ans[mask]时,记得更新最高位大于i的k,对应的dp状态。
d
p
[
m
a
s
k
⨁
(
1
<
<
k
)
]
[
k
]
+
=
a
n
s
[
m
a
s
k
]
dp[mask\bigoplus (1<<k)][k]+=ans[mask]
dp[mask⨁(1<<k)][k]+=ans[mask]
对于每次查询,我们计算两种种类奶牛的mask1,mask2,方案数为 a n s [ m a s k 1 ] ∗ a n s [ m a s k 2 ] ans[mask1]*ans[mask2] ans[mask1]∗ans[mask2]
挺难想到的,脑壳疼>.<
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 20;
ll dp[1<<N][N];
ll ans[1<<N];
string op;
int n, x, g[N];
int main() {
cin >> n;
for (int i = 0; i < n; ++i) {
bool flag = true;
for (int j = 0; j < n; ++j) {
cin >> x;
--x;
if (!flag) {
continue;
}
g[i] ^= (1 << x);// 存储所有奶牛i能吃的小麦x
if (x == i) {
flag = false;
}
}
}
ans[0] = 1;
for (int k = 0; k < n; ++k) {
dp[1<<k][k] = 1;
}
for (int i = 0; i < n; ++i) {
for (int mask = (1 << i); mask < (1 << (i + 1)); ++mask) {
for (int last = 0; last <= i; ++last) {
// 过滤不在当前集合的奶牛
if (!(mask & (1 << last))) {
continue;
}
ll val = dp[mask][last];
for (int k = 0; k < i; ++k) {
// 不在mask中且奶牛可以吃的小麦k,进行状态转移
if (!(mask & (1 << k)) && (g[last] & (1 << k))) {
dp[mask^(1<<k)][k] += val;
}
}
// 如果奶牛last可以吃小麦i,则mask闭环了
if (g[last] & (1 << i)) {
ans[mask] += val;
}
}
// 计算完ans[mask] 更新高位mask的dp
for (int k = i + 1; k < n; ++k) {
dp[mask^(1<<k)][k] += ans[mask];
}
}
}
int q;
cin >> q;
while (q--) {
cin >> op;
int h = 0, y = 0;
for (int i = 0; i < n; ++i) {
if (op[i] == 'H') {
h ^= (1 << i);
} else {
y ^= (1 << i);
}
}
cout << ans[h] * ans[y] << endl;
}
}
/*
*/