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; }