#状压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]);
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13529415.html