需要在 (n) 个人中选出 (p) 个人站着队伍中的各个位置上,再从剩下的 (n-p) 个人中选出 (k) 个人作为观众。第 (i) 个人作为观众可以有 (a_i) 的得分,作为队伍中第 (j) 个位置上的人可以有 (s_{i,j}) 的得分,求得分的最大值。(nleq 10^5, pleq 7)
Solution
将人按照 (a) 从大到小排序,这样如果敲定了哪些人是队伍,那么剩下的靠前的 (k) 个一定是观众
考虑状压 DP,设 (f[i][j]) 表示考虑了前 (i) 个人,队伍中各个位置的状态为 (j) 的得分最大值,决策下一个人放在队伍的哪一个位置,或者不入队,如果不入队则判定如果 (i-popcount(j) leq k) 则去当观众
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
struct item {
int s[8],a;
bool operator < (const item &b) {
return a>b.a;
}
} a[N];
int n,p,k,f[500],g[500];
signed main() {
ios::sync_with_stdio(false);
cin>>n>>p>>k;
for(int i=1;i<=n;i++) cin>>a[i].a;
for(int i=1;i<=n;i++) {
for(int j=1;j<=p;j++) cin>>a[i].s[j];
}
sort(a+1,a+n+1);
memset(g,-0x3f,sizeof g);
g[0]=0;
for(int i=1;i<=n;i++) {
memset(f,-0x3f,sizeof f);
for(int j=0;j<1<<p;j++) {
for(int l=1;l<=p;l++) {
if((j&(1<<(l-1)))) {
int x=j^(1<<(l-1));
f[j]=max(f[j],g[x]+a[i].s[l]);
}
}
f[j]=max(f[j],g[j]+(i-__builtin_popcount(j)<=k?a[i].a:0));
}
memcpy(g,f,sizeof f);
}
cout<<f[(1<<p)-1];
}