題目鏈接:https://codeforces.com/contest/1316/problem/E
題目大意:
有n個人,要選一支排球隊伍,一共有p個位置,每個位置都需要一個人,同時需要k個觀衆,一個隊員只能去一個位置,而且每個人要麼去排球隊要麼去觀衆要麼什麼也不幹,只能有一個身份,給出每個人當觀衆的值以及在每個位置上的值,求和最大值。
題目思路:
看到p的範圍這麼小,第一個想法肯定就是狀壓dp了,通常有這種異常小的數據範圍都會這麼想。。那麼dp肯定有一維是枚舉狀態了,那麼觀衆咋辦呢?有個很巧妙的處理。可以發現一個事情,就是如果p個人已經選好了,那麼觀衆選誰也就顯而易見了,肯定是剩下的人裏面最強的k個鴨!所以我們可以對觀衆根據觀衆值進行排序,然後dp轉移的時候,直接根據,除了去打排球以外還有多少人,如果還缺人,直接上,因爲除了隊伍裏的人,剩下的是越強的去越好,所以按大到小排序的話,那直接按照強的人先安排的原則貪心,就能搞定。所以dp轉移很簡單,枚舉狀態j,然後用i-j的位數,也就是除了去打排球的還剩多少人,如果剩餘人數小於等於k,那麼美滋滋,所有人都能去打排球,否則的話大佬觀衆已經夠了就別進去摻和了。然後就是很套路的dp轉移啦,直接枚舉j這個狀態枚舉自己能頂替誰就行。
坑點一個是需要從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]);
}
}