[CF1316E] Team Building

需要在 (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];
}

原文地址:https://www.cnblogs.com/mollnn/p/12460865.html