题意
从 (n) 个人中选 (k) 个观众,(p) 个不同位置的比赛选手,每个人做观众或比赛选手都能为队伍提升不同的强度,
问队伍的最大强度是多少。
题解
因为 (p) 很小,所以 (p) 个位置的比赛选手可以状态压缩DP来选择。
而如果把所有人以做观众能提供的强度从大到小排序之后,(k) 个观众一定是从前 (k+p) 个人中选择,
因为如果前 (k+p) 之外有人做了观众,那么前 (k+p) 个人中肯定有人空闲并且做观众提供的强度比他更高。
那么只有当前 (i-1) 个人中观众数量小于 (k) 并且 (ile k+p) 的时候第 (i) 个人才可以做观众。
这道题还可以用费用流过,在官方题解的第一个讨论里面的大神有代码,
我按最简单的方式建图跑费用流超时超的飞起,大神的思路我又没看懂,嘤语太渣
希望用费用流做出来的大神可以留言一下为蒟蒻解答疑惑
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
const int M=1e6+10;
int n,p,k;
struct Node{
int a,p[7];
}s[N];
bool cmp(Node x,Node y){return x.a>y.a;}
LL f[N][1<<7];
int main(){
scanf("%d%d%d",&n,&p,&k);
for(int i=1;i<=n;i++) scanf("%d",&s[i].a);
for(int i=1;i<=n;i++) for(int j=0;j<p;j++) scanf("%d",&s[i].p[j]);
sort(s+1,s+n+1,cmp);
memset(f,0xc0,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++)
for(int sta=0;sta<(1<<p);sta++){
int cnt=0;for(int k=0;k<p;k++) cnt+=(bool)(sta&(1<<k));
if(i-cnt<=k&&i<=k+p) f[i][sta]=max(f[i][sta],f[i-1][sta]+s[i].a);
else f[i][sta]=f[i-1][sta];
for(int k=0;k<p;k++)
if(sta&(1<<k)) f[i][sta]=max(f[i][sta],f[i-1][sta-(1<<k)]+s[i].p[k]);
}
cout<<f[n][(1<<p)-1]<<endl;
return 0;
}