CodeCraft-20 (Div. 2) E. Team Building(狀壓DP)

題目鏈接:https://codeforces.com/contest/1316/problem/E

 

題目大意:

  有n個人,要選一支排球隊伍,一共有p個位置,每個位置都需要一個人,同時需要k個觀衆,一個隊員只能去一個位置,而且每個人要麼去排球隊要麼去觀衆要麼什麼也不幹,只能有一個身份,給出每個人當觀衆的值以及在每個位置上的值,求和最大值。n1e5,p7,p+k1e5n≤1e5,p≤7,p+k≤1e5

 

題目思路:

  看到p的範圍這麼小,第一個想法肯定就是狀壓dp了,通常有這種異常小的數據範圍都會這麼想。。那麼dp肯定有一維是枚舉狀態了,那麼觀衆咋辦呢?有個很巧妙的處理。可以發現一個事情,就是如果p個人已經選好了,那麼觀衆選誰也就顯而易見了,肯定是剩下的人裏面最強的k個鴨!所以我們可以對觀衆根據觀衆值進行排序,然後dp轉移的時候,直接根據,除了去打排球以外還有多少人,如果還缺人,直接上,因爲除了隊伍裏的人,剩下的是越強的去越好,所以按大到小排序的話,那直接按照強的人先安排的原則貪心,就能搞定。所以dp轉移很簡單,枚舉狀態j,然後用i-j的位數,也就是除了去打排球的還剩多少人,如果剩餘人數小於等於k,那麼美滋滋,所有人都能去打排球,否則的話大佬觀衆已經夠了就別進去摻和了。然後就是很套路的dp轉移啦,直接枚舉j這個狀態枚舉自己能頂替誰就行。
  坑點一個是需要從0開始枚舉起,因爲有可能前面幾個人大家都去當觀衆了,另一個坑點是初始化的時候除了dp[0][0]dp[0][0]設爲0,其他都要設爲-inf,不然的話會出現非法狀態的轉移,過不了樣例3。

 

以下是代碼:

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
const ll MAXN =1e5+5;
const ll MAXM = 4e7+5;
const ll MOD = 998244353;
int n,p,k;
struct node{
    int pos;
    ll val;
}a[MAXN];
ll s[MAXN][10];
ll dp[MAXN][300];
bool cmp(node a,node b){
    return a.val>b.val;
}
int main()
{
    while(~scanf("%d%d%d",&n,&p,&k)){
        memset(dp,-0x3f,sizeof(dp));
        dp[0][0]=0;
        rep(i,1,n)a[i].pos=i,scanf("%I64d",&a[i].val);
        sort(a+1,a+n+1,cmp);
        rep(i,1,n){
            rep(j,1,p)scanf("%I64d",&s[i][j]);
        }
        int endd=(1<<p)-1;
        rep(i,1,n){
            rep(j,0,endd){
                int num=i-__builtin_popcount(j);
                if(num<0)continue;
                dp[i][j]=dp[i-1][j];
                if(num<=k){
                    dp[i][j]+=a[i].val;
                }
                rep(k,0,p-1){
                    if((1<<k)&j){
                        dp[i][j]=max(dp[i][j],dp[i-1][j-(1<<k)]+s[a[i].pos][k+1]);
                    }
                }
            }
        }
        printf("%I64d\n",dp[n][endd]);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章