#状压dp,贪心#CF1316E Team Building
题目
为了组织一支排球队,你需要为队伍里的\(p\)个不同的位置,从\(n\)个人中选出\(p\)个人,
且每个位置上都恰好有一个人。另外还需要从剩下的人中选出恰好\(k\)个人作为观众。
对于第\(i\)个人,已知他作为观众时能为队伍增加\(a_i\)点力量,
还有他在队伍的第\(j\)个位置上时能为队伍增加\(s_{i,j}\)点力量。
请问这只排球队力量的最大值是多少?
分析
由于\(p\)很小,考虑状压dp,
设\(dp[i][s]\)表示前\(i\)个人选取为队员的状态为\(s\)时产生的最大力量
为了让观众力量更大,按照贪心思想肯定要先按观众力量从大到小排序,
还要保证选取的观众人数恰好等于\(k\),最后输出\(dp[n][2^p-1]\)
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=100011; typedef long long lll;
struct rec{int w,z[7];}a[N];
int n,m1,m2,al,xo[N]; lll dp[128];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
bool cmp(rec x,rec y){return x.w>y.w;}
inline void Max(lll &a,lll b){a=a>b?a:b;}
signed main(){
n=iut(),m1=iut(),m2=iut(),al=1<<m1;
for (rr int i=1;i<al;++i) xo[i]=xo[i&(i-1)]+1;
for (rr int i=1;i<=n;++i) a[i].w=iut();
for (rr int i=1;i<=n;++i)
for (rr int j=0;j<m1;++j)
a[i].z[j]=iut();
sort(a+1,a+1+n,cmp);
memset(dp,0xcf,sizeof(dp)),dp[0]=0;
for (rr int i=1;i<=n;++i)
for (rr int j=al-1;~j;--j){
if (dp[j]>=0) dp[j]+=a[i].w*(i-xo[j]<=m2);
for (rr int p=0;p<m1;++p) if ((j>>p)&1)
Max(dp[j],dp[j^(1<<p)]+a[i].z[p]);
}
return !printf("%lld",dp[al-1]);
}