1. 程式人生 > 實用技巧 >#狀壓dp,貪心#CF1316E Team Building

#狀壓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]);
}