标签:return its building for 还需要 build col namespace operator
你需要组建一支排球队。为了组织一支排球队,你需要为队伍里的 $p$ 个不同的位置,从 $n$ 个人中选出 $p$ 个人,且每个位置上都恰好有一个人。另外还需要从剩下的人中选出恰好 $k$ 个人作为观众。
对于第 $i$ 个人,已知他作为观众时能为队伍增加 $a_i$ 点力量,还有他在队伍的第 $j$ 个位置上时能为队伍增加 $s_{i,j}$ 点力量。
请问这只排球队力量的最大值是多少?
看到 $p$ 很小,考虑状态压缩 DP。
这里先把每个人按照 $a_i$ 降序排序。
设想,对于一个状态 $f_{i, s}$,$s$ 是一个 $p$ 位二进制数,表示队伍的选择情况,当前这个 $i$ 会不会被选进观众呢?
这里用到一个贪心,如果 $i \le \operatorname{CountBit}(s) + k$,那么,我们就会把 $i$ 选到观众里面去。
这很好理解,原本应该是选前 $k$ 个当观众,但是有 $\operatorname{CountBit}(s)$ 个已经被选到队伍里面去了,所以我们把限制往后移这么多位。
接下来,考虑把 $i$ 弄成队员。我们枚举一个 $s$ 包含的位置 $t$,试图把 $i$ 放进去,那么就要用 $f_{i-1, s \oplus 2^t}$ 来更新 $f_{i, s}$。
这题作为状态压缩 DP 还是比较好实现的,时间复杂度 $\mathcal O(n \cdot 2^p)$,具体细节可以看代码。代码中用 $j$ 代替了 $s$。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, P = 130;
struct people
{
LL a, s[7];
bool operator < (const people &oth) const { return a > oth.a; }
} peo[N];
int n, p, k;
inline int CountBit(int x)
{
int res = 0;
while(x) res++, x &= x - 1;
return res;
}
LL f[N][P];
int main()
{
scanf("%d %d %d", &n, &p, &k);
for(int i = 1; i <= n; i++) scanf("%lld", &peo[i].a);
for(int i = 1; i <= n; i++)
for(int j = 0; j < p; j++)
scanf("%lld", &peo[i].s[j]);
sort(peo + 1, peo + n + 1);
int full = 1 << p;
memset(f, -0x3f, sizeof f);
f[0][0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < full; j++)
{
int cnt = CountBit(j);
f[i][j] = f[i - 1][j];
if(i <= cnt + k) f[i][j] = max(f[i][j], f[i - 1][j] + peo[i].a);
for(int t = 0; t < p; t++) if(j >> t & 1)
f[i][j] = max(f[i][j], f[i - 1][j ^ (1 << t)] + peo[i].s[t]);
}
}
printf("%lld\n", f[n][full - 1]);
return 0;
}
CodeForces 1316E Team Building
标签:return its building for 还需要 build col namespace operator
原文地址:https://www.cnblogs.com/syksykCCC/p/CF1316E.html