CodeCraft-20 (Div. 2) E.Team Building

題目鏈接
大意:n個人,選k個觀衆,p個球員,每個人當觀衆的能力爲aia_i,在每個位置的能力爲si,js_{i,j},讓你選擇p+kp+k個人使得能力和最大。
思路:考慮狀壓dpdpf[i][j]f[i][j]表示到第ii個人時的第jj個狀態的最大能力,狀態表示每個位置被選與否。
那麼轉移時只需要考慮兩個地方:
1.前i1i-1個人是否選滿了kk個觀衆
2.在jj狀態下還有哪個位置沒人上
觀察第一個地方,我們要使得我們選的觀衆能力和是當前jj狀態下最大的,那麼我們需要將所有人按照aia_i非遞增順序排列才能滿足這一點,那麼選的聽衆肯定就是能選的人中能力值最大的。
注意所有轉移都要從合法情況轉移得到。
初始只有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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章