codeforces#1316E. Team Building(状压dp)

题目链接:

https://codeforces.com/contest/1316/problem/E

题意:

 有$n$个人,每个人可以作为运动员和观众,相应位置会有一个权值

 只能选择$p$个运动员和$k$个观众,求最大权值

分析:

 定义$dp[i][j]$为考虑前$i$个人,运动员选择情况为$j$的最优解

 按照作为观众的权值从大到小排序,这样$dp[i-1][j]$的运动员都是在$1$到$i-1$中选择的,保证了转移的可行性

 否则,同样$dp[i-1][j]$,有可能选择的是作为观众权值较小的人,这样的最大值不一定就是真正的最优解

 转移时考虑当前位置是否已经被选择为观众,如果已经选择为观众,那么减去作为观众的权值即可

 AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef long long ll;
typedef vector<int> VI;
typedef pair<int,int> PII;

const ll mod=1e6+7;
const int maxn=1e5+7;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

PII a[maxn];
ll dp[maxn][(1<<7)+7];
int n,p,k,b[maxn][8];
int cal(int x){
    int res=0;
    while(x){
        if(x&1)res++;
        x/=2;
    }
    return res;
}
int main(){
    scanf("%d %d %d",&n,&p,&k);
    rep(i,1,n){
        scanf("%d",&a[i].fi);
        a[i].fi=-a[i].fi;
        a[i].se=i;
    }
    sort(a+1,a+1+n);
    rep(i,1,n)a[i].fi*=-1;
    rep(i,1,n)rep(j,1,p)scanf("%d",&b[i][j]);

    rep(i,1,k)dp[0][0]+=a[i].fi;
    rep(i,1,n)dp[i][0]=dp[0][0];
    rep(i,1,n){
        rep(j,1,(1<<p)-1){
            dp[i][j]=dp[i-1][j];
            int cnt=cal(j);
            rep(l,0,p-1){
                if(j&(1<<l)){
                    if(cnt-1+k>=i)
                        dp[i][j]=max(dp[i][j],dp[i-1][j-(1<<l)]+b[a[i].se][l+1]-a[i].fi+a[cnt+k].fi);

                    else
                        dp[i][j]=max(dp[i][j],dp[i-1][j-(1<<l)]+b[a[i].se][l+1]);
                }
            }
        }
    }
    printf("%lld
",dp[n][(1<<p)-1]);
    return 0;
}

  

原文地址:https://www.cnblogs.com/carcar/p/12431572.html