[CF1316E] Team Building - 状压dp

需要在 \(n\) 个人中选出 \(p\) 个人站着队伍中的各个位置上,再从剩下的 \(n-p\) 个人中选出 \(k\) 个人作为观众。第 \(i\) 个人作为观众可以有 \(a_i\) 的得分,作为队伍中第 \(j\) 个位置上的人可以有 \(s_{i,j}\) 的得分,求得分的最大值。\(n\leq 10^5, p\leq 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];
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/12460865.html