E - Team Building 状压dp

题意:给n个人,从中挑出p个参赛人,k个观众,每个人作为观众有对应的贡献,每个人作为参赛人在不同位置上也有对应的贡献,问最大贡献是多少?

分析:考虑dp[i][j],表示前 i 个人选的参赛人状态为 j 的最大贡献( j 中状态为1 表示第 i 个人考虑作为参赛人)。

   假设我们已经选好了参赛人的贡献,那么观众是不是就是一定事选剩下人作为观众贡献最大的那一些人;

   明白了这一点后,我们必然先把n个人按照观众的贡献降序排起来;

   考虑转移,初始状态就是我把第1~k个人作为观众来加到初始贡献上,那么转移就是挑出一个作为参赛人,观众就变成了k-1个,那么得考虑加进来一个,那么加进来的那一个肯定是第k+1个人,如此推下去。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e5+5;
const int maxn=130;
ll dp[M][maxn],cnt[maxn];
struct node{
    ll val,s[10];
    bool operator<(const node &b)const{
        return val>b.val;
    }
}a[M];
int n,p,k;
ll cal(int cur,int c){
    if(k+c<=cur)
        return 0;
    return a[k+c].val-a[cur].val;
}
int main(){
    
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i].val);
    }    
    for(int i=1;i<=n;i++)
        for(int j=0;j<p;j++)
            scanf("%lld",&a[i].s[j]);
    sort(a+1,a+1+n);
    memset(dp,172,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=k;i++)
        dp[0][0]+=a[i].val;
    int mx=1<<p;
    ///cnt[i]表示i的二进制位有多少 
    for(int i=0;i<mx;i++)
        cnt[i]=cnt[i>>1]+(i&1);
    for(int i=1;i<=n;i++)
        for(int j=0;j<mx;j++){
            dp[i][j]=dp[i-1][j];
            for(int k=0;k<p;k++){
                if(j&(1<<k)){
                    dp[i][j]=max(dp[i][j],dp[i-1][j^(1<<k)]+a[i].s[k]+cal(i,cnt[j]));
                }
            }
        }
    printf("%lld\n",dp[n][mx-1]);
    return 0;
}
View Code

 

posted @ 2020-03-05 20:01  starve_to_death  阅读(154)  评论(0编辑  收藏  举报