Codeforces 1316 E. Team Building —— 状压DP+贪心

This way

题意:

现在有n个人,要从里面挑选p个人当做队员,k个人当做观众。
每个人当做观众时会有一个贡献a[i],当做队员时,站在不同的位置会有不同的贡献s[i][j],每个人只能被选成某个类型一次,队员的每个位置都只能站一个人
问你挑选完后最大的值是多少。

题解:

一眼就是状压dp
dp[i][j]表示到了第i个人,已经站的位置的状态为j的最大贡献。
但是我一下子还突然想不到怎么看当前的人是否是那前k大的a中的一个,还被卡了一会
转念一想排个序不就好了,然后枚举人的同时枚举已经站的位置情况,然后减一下就知道是否已经选了k个了

#include
using namespace std;
#define ll long long
const int N=1e5+5,M=(1<<7)+5;
struct node{
    ll a,s[10];
    bool operator< (const node& aa)const {
        return a>aa.a;
    }
}a[N];
ll dp[N][M];
int main()
{
    int n,p,k;
    scanf("%d%d%d",&n,&p,&k);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i].a);
    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,-1,sizeof(dp));
    dp[0][0]=0;
    int mx=1<<p;
    for(int i=1;i<=n;i++){
        for(int s=0;s<mx;s++){
            if(dp[i-1][s]==-1)continue;
            int num=__builtin_popcount(s);
            if(i-num-1<k)
                dp[i][s]=max(dp[i][s],dp[i-1][s]+a[i].a);
            else
                dp[i][s]=max(dp[i][s],dp[i-1][s]);
            for(int j=0;j<p;j++){
                if(!(s&(1<<j))){
                    dp[i][s^(1<<j)]=max(dp[i][s^(1<<j)],dp[i-1][s]+a[i].s[j]);
                }
            }
        }
    }
    printf("%lld\n",dp[n][mx-1]);
    return 0;
}

你可能感兴趣的:(想法,贪心,dp)