CodeForces 1316E. Team Building 【贪心、状压Dp】

传送门

题意

\(n\) 个人中选 \(k\) 个观众,\(p\) 个不同位置的比赛选手,每个人做观众或比赛选手都能为队伍提升不同的强度,
问队伍的最大强度是多少。

题解

因为 \(p\) 很小,所以 \(p\) 个位置的比赛选手可以状态压缩DP来选择。
而如果把所有人以做观众能提供的强度从大到小排序之后,\(k\) 个观众一定是从前 \(k+p\) 个人中选择,
因为如果前 \(k+p\) 之外有人做了观众,那么前 \(k+p\) 个人中肯定有人空闲并且做观众提供的强度比他更高。
那么只有当前 \(i-1\) 个人中观众数量小于 \(k\) 并且 \(i\le 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;
}
posted @ 2020-03-05 14:33  BakaCirno  阅读(229)  评论(0编辑  收藏  举报