Codeforces 1316 E. Team Building(状压dp)

题目链接
题目大意:
总共有n个人,每个人可以当做观众或者球员,每个位置只有一个球员,给定每个人当观众的价值和每个人在每个位置的价值。其中共有p(p<=7)个球员和k个观众,求出怎么安排使得总价值最大。

解题思路:
首先贪心一下,把他们按观众价值从大到小排序,所以在观众人数还没满的情况下如果不当球员就一定会当观众。然后进行状压dp,状态为dp[i][sta],表示第i个人,其中状态sta上二进制为1的地方表示这个位置已经有球员了。
所以状态转移为:
首先:dp[i][sta] = dp[i-1][sta]继承上一个人的状态
当观众:
观众还有余量的时候
if (i-cal_num(sta)<=k)
dp[i][sta]=max(dp[i][sta],dp[i-1][sta]+a[i].a);

当球员:
踢第j个位置,则这个状态必须在第j个有球员。
if(sta \oplus (1<<j)==1),
dp[i][sta]=max(dp[i][sta],dp[i-1][sta \oplus (1<<j)]+people.p[j]),

解题代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll inf=1e18;
struct P
{
    ll a,s[7];
    friend bool operator<(P t1,P t2)
    {
        return t1.a>t2.a;
    }
}a[maxn];
ll dp[maxn][1<<8];
int cal_num(int sta)
{
    int ret=0;
    while (sta)
    {
        if (sta&1)
        ret++;
        sta>>=1;
    }
    return ret;
}
int main()
{
    ll n,p,k;
    scanf("%I64d%I64d%I64d",&n,&p,&k);
    for (int i=1;i<=n;i++)
    scanf("%I64d",&a[i].a);
    for (int i=1;i<=n;i++)
    {
        for (int j=0;j<p;j++)
        scanf("%I64d",&a[i].s[j]);
    }
    sort(a+1,a+1+n);
    //不要忘记初始化
    for(int sta=0;sta<(1<<p);sta++)
    dp[0][sta]=-inf;
    dp[0][0]=0;
    for (int i=1;i<=n;i++)
    {
        for (int sta=0;sta<(1<<p);sta++)
        {
            dp[i][sta]=dp[i-1][sta];
            if (i-cal_num(sta)<=k)
                dp[i][sta]=max(dp[i][sta],dp[i-1][sta]+a[i].a);
            for (int j=0;j<p;j++)
            {
                if ((1<<j)&sta)
                {
                    dp[i][sta]=max(dp[i][sta],dp[i-1][sta^(1<<j)]+a[i].s[j]);
                }
            }
        }
    }
    cout<<dp[n][(1<<p)-1];
}

发布了12 篇原创文章 · 获赞 1 · 访问量 327

猜你喜欢

转载自blog.csdn.net/qq_41818939/article/details/104682080