习题: 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;
}

浙公网安备 33010602011771号