1. 程式人生 > 實用技巧 >習題: Team Building(狀壓DP)

習題: Team Building(狀壓DP)

題目

傳送門

思路

首先有一個性質,如果我們已經確定了哪些人作為隊員,那麼其餘的觀眾一定是貪心地從大到小的去選

首先將人按他們在觀眾上能提供的貢獻進行排序

\(dp[i][j]\)為前i個人,排球隊員的狀態為j

再有一個性質,如果我們設j中為cnt個1,

如果i>cnt+k,那麼第i個人一定不會被選為觀眾(這應該很好理解吧)

也就是指只有在\(i<=cnt+k\)的時候,我們才考慮i這個人作為觀眾的貢獻,

如果是i這個人作為排球隊員,直接暴力轉移即可

程式碼

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,p,k;
long long dp[100005][(1<<8)];
struct node
{
	long long val;
	long long s[8];
	friend bool operator < (const node &a,const node &b)
	{
		return a.val>b.val;
	}
}a[100005];
int calc(int val)
{
	int ret=0;
	while(val)
	{
		ret=ret+(val&1);
		val>>=1;
	}
	return ret;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>p>>k;
	for(int i=1;i<=n;i++)
		cin>>a[i].val;
	for(int i=1;i<=n;i++)
		for(int j=0;j<p;j++)
			cin>>a[i].s[j];
	sort(a+1,a+n+1);
	memset(dp,-0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<(1<<p);j++)
		{
			dp[i][j]=max(dp[i][j],dp[i-1][j]);
			if(i<=calc(j)+k)
				dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i].val);
			for(int t=0;(1<<t)<(1<<p);t++)
			{
				if((j&(1<<t))==0)
				{
					dp[i][j|(1<<t)]=max(dp[i][j|(1<<t)],dp[i-1][j]+a[i].s[t]);
				}
			}
		}
	}
	cout<<dp[n][(1<<p)-1];
	return 0;
}