题意
n
n
n 个人,
p
p
p 个位置,选
p
p
p 人作为每个位置的运动员,再选
k
k
k 个作为观众。
每个人作为运动员在每个位置的权值
s
i
,
p
s_{i,p}
si,p 和每个人作为观众权值
a
i
a_i
ai 已知。
n
≤
100000
,
p
≤
7
n\leq 100000, p \leq 7
n≤100000,p≤7
分析
这题并不难想,可惜比赛时没时间了QAQ
首先假设选完运动员,我们选观众肯定是从大到小选的。
所以先将
a
i
a_i
ai 从大到小排序。
然后我们考虑一下最终选出人的集合。
首先前面一部分肯定是连续的,即是
1
,
2...
t
{1,2...t}
1,2...t 都被选。因为如果有没被选的,可以让他和最后一个观众交换,结果一定更优。
再看一下,作为观众的
k
k
k 个人一定是在前
k
+
p
k+p
k+p 个人中选出来的,原理同上。
我们设
f
i
,
j
f_{i,j}
fi,j 表示前
i
i
i 个人,选出的位置集合为
j
j
j 的最大值。
所以当
i
>
k
+
p
i > k+p
i>k+p 时,每个人都只能作为运动员,转移方程为:
f
i
,
j
=
max
{
f
i
−
1
,
j
−
(
1
<
<
l
)
+
v
p
i
,
l
+
1
(
l
∈
j
)
}
f_{i,j}=\max\{f_{{i-1,j -(1<<l)}}+v_{p_i,l+1}~(l\in j)\}
fi,j=max{fi−1,j−(1<<l)+vpi,l+1 (l∈j)}
其中
p
p
p 是第
i
i
i 个人的原编号。
再考虑
i
≤
k
+
p
i\leq k+p
i≤k+p 时,转移分两种情况:
- 第
i
i
i 个人可以作为观众,考虑需满足的条件:记
c
n
t
cnt
cnt 为集合
j
j
j 中元素个数,那么
i
≤
c
n
t
+
k
i\leq cnt + k
i≤cnt+k,转移方程为:
f i , j = max { f i − 1 , j + a i } f_{i,j}=\max\{f_{{i-1},j}+a_i\} fi,j=max{fi−1,j+ai} - 第
i
i
i 个人为运动员,转移方程:
f i , j = max { f i − 1 , j − ( 1 < < l ) + v p i , l + 1 ( l ∈ j ) } f_{i,j}=\max\{f_{{i-1,j -(1<<l)}}+v_{p_i,l+1}~(l\in j)\} fi,j=max{fi−1,j−(1<<l)+vpi,l+1 (l∈j)}
代码如下
#include <bits/stdc++.h>
#define N 100005
#define LL long long
using namespace std;
struct node{
int i, v;
bool operator < (const node & A) const{
return v > A.v;
}
}d[N];
LL z = 1, f[N][(1 << 7)];
int v[N][8];
int count(int x){
int s = 0;
while(x){
if(x & 1) s++;
x >>= 1;
}
return s;
}
int main(){
int i, j, t, l, n, m, p, k, cnt;
scanf("%d%d%d", &n, &p, &k);
for(i = 1; i <= n; i++){
scanf("%d", &d[i].v);
d[i].i = i;
}
sort(d + 1, d + i);
for(i = 1; i <= n; i++){
for(j = 1; j <= p; j++) scanf("%d", &v[i][j]);
}
memset(f, 0xc0, sizeof(f));
f[0][0] = 0;
for(i = 1; i <= n; i++){
for(j = 0; j < (1 << p); j++){
cnt = count(j);
f[i][j] = f[i - 1][j];
if(i <= p + k && i - cnt <= k) f[i][j] = max(f[i][j], f[i - 1][j] + d[i].v);
for(l = 0; l < p; l++){
if(1 << l & j) f[i][j] = max(f[i][j], f[i - 1][j ^ (1 << l)] + v[d[i].i][l + 1]);
}
}
}
printf("%lld", f[n][(1 << p) - 1]);
return 0;
}