题目链接
大意:n个人,选k个观众,p个球员,每个人当观众的能力为 a i a_i ai,在每个位置的能力为 s i , j s_{i,j} si,j,让你选择 p + k p+k p+k个人使得能力和最大。
思路:考虑状压 d p dp dp f [ i ] [ j ] f[i][j] f[i][j]表示到第 i i i个人时的第 j j j个状态的最大能力,状态表示每个位置被选与否。
那么转移时只需要考虑两个地方:
1.前 i 1 i-1 i1个人是否选满了 k k k个观众
2.在 j j j状态下还有哪个位置没人上
观察第一个地方,我们要使得我们选的观众能力和是当前 j j j状态下最大的,那么我们需要将所有人按照 a i a_i ai非递增顺序排列才能满足这一点,那么选的听众肯定就是能选的人中能力值最大的。
注意所有转移都要从合法情况转移得到。
初始只有 f [ 0 ] [ 0 ] f[0][0] 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;
}