CodeForces 1316E. Team Building 【贪心、状压Dp】

传送门

题意

\(n\) 个人中选 \(k\) 个观众,\(p\) 个不同位置的比赛选手,每个人做观众或比赛选手都能为队伍提升不同的强度,
问队伍的最大强度是多少。

题解

因为 \(p\) 很小,所以 \(p\) 个位置的比赛选手可以状态压缩DP来选择。
而如果把所有人以做观众能提供的强度从大到小排序之后,\(k\) 个观众一定是从前 \(k+p\) 个人中选择,
因为如果前 \(k+p\) 之外有人做了观众,那么前 \(k+p\) 个人中肯定有人空闲并且做观众提供的强度比他更高。
那么只有当前 \(i-1\) 个人中观众数量小于 \(k\) 并且 \(i\le k+p\) 的时候第 \(i\) 个人才可以做观众。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
const int M=1e6+10;
int n,p,k;
struct Node{
    int a,p[7];
}s[N];
bool cmp(Node x,Node y){return x.a>y.a;}
LL f[N][1<<7];

int main(){
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=n;i++) scanf("%d",&s[i].a);
    for(int i=1;i<=n;i++) for(int j=0;j<p;j++) scanf("%d",&s[i].p[j]);
    sort(s+1,s+n+1,cmp);
    memset(f,0xc0,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++)
        for(int sta=0;sta<(1<<p);sta++){
            int cnt=0;for(int k=0;k<p;k++) cnt+=(bool)(sta&(1<<k));
            if(i-cnt<=k&&i<=k+p) f[i][sta]=max(f[i][sta],f[i-1][sta]+s[i].a);
            else f[i][sta]=f[i-1][sta];
            for(int k=0;k<p;k++)
                if(sta&(1<<k)) f[i][sta]=max(f[i][sta],f[i-1][sta-(1<<k)]+s[i].p[k]);
        }
    cout<<f[n][(1<<p)-1]<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/BakaCirno/p/12420098.html