题目链接
大意:n个人,选k个观众,p个球员,每个人当观众的能力为 ai,在每个位置的能力为 si,j,让你选择 p+k个人使得能力和最大。
思路:考虑状压 dp, f[i][j]表示到第 i个人时的第 j个状态的最大能力,状态表示每个位置被选与否。
那么转移时只需要考虑两个地方:
1.前 i−1个人是否选满了 k个观众
2.在 j状态下还有哪个位置没人上
观察第一个地方,我们要使得我们选的观众能力和是当前 j状态下最大的,那么我们需要将所有人按照 ai非递增顺序排列才能满足这一点,那么选的听众肯定就是能选的人中能力值最大的。
注意所有转移都要从合法情况转移得到。
初始只有 f[0][0]合法。
细节见代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
int n,p,k,a[N],s[N][8],d[N],vis[N];
LL f[N][128];
LL sum=0;
int main() {
ios::sync_with_stdio(false);
cin>>n>>p>>k;
for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i],d[i]=i;
for(int i=1;i<=n;i++){
for(int j=1;j<=p;j++){
cin>>s[i][j];
}
}
memset(f,-1,sizeof f);
f[0][0]=0;//初始
sort(d+1,d+1+n,[](int x,int y){
return a[x]>a[y];//排序
});
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<p);j++){
int tmp=0;
for(int q=0;q<p;q++){
if((1<<q)&j)tmp++;
}
if(i-1-tmp>=k&&f[i-1][j]!=-1)f[i][j]=f[i-1][j];//选满了
else if(i-1-tmp<k&&f[i-1][j]!=-1)f[i][j]=f[i-1][j]+a[d[i]];//没选满
}
for(int j=0;j<(1<<p);j++){
for(int q=0;q<p;q++){
if(!((1<<q)&j) && f[i-1][j]!=-1){
f[i][j|(1<<q)]=max(f[i][j|(1<<q)],f[i-1][j]+s[d[i]][q+1]);//q这个位置还缺人
}
}
}
}
cout<<f[n][(1<<p)-1]<<'\n';
return 0;
}