CF1316E Team Building(状压dp)

博客介绍了CF1316E问题的解决思路,涉及团队选择优化问题,利用动态规划(状压DP)进行求解。首先对观众权值进行降序排序,接着分析最优解的性质,提出状态定义为前i个人选择的位置集合为j的最大价值。详细阐述了状态转移方程,并区分了当i超过k+p和不超过k+p时的不同情况。最后给出了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

n n n 个人, p p p 个位置,选 p p p 人作为每个位置的运动员,再选 k k k 个作为观众。
每个人作为运动员在每个位置的权值 s i , p s_{i,p} si,p 和每个人作为观众权值 a i a_i ai 已知。
n ≤ 100000 , p ≤ 7 n\leq 100000, p \leq 7 n100000,p7

分析

这题并不难想,可惜比赛时没时间了QAQ
首先假设选完运动员,我们选观众肯定是从大到小选的。
所以先将 a i a_i ai 从大到小排序。
然后我们考虑一下最终选出人的集合。
首先前面一部分肯定是连续的,即是 1 , 2... t {1,2...t} 1,2...t 都被选。因为如果有没被选的,可以让他和最后一个观众交换,结果一定更优。
再看一下,作为观众的 k k k 个人一定是在前 k + p k+p k+p 个人中选出来的,原理同上。
我们设 f i , j f_{i,j} fi,j 表示前 i i i 个人,选出的位置集合为 j j j 的最大值。
所以当 i > k + p i > k+p i>k+p 时,每个人都只能作为运动员,转移方程为:
f i , j = max ⁡ { f i − 1 , j − ( 1 < < l ) + v p i , l + 1   ( l ∈ j ) } f_{i,j}=\max\{f_{{i-1,j -(1<<l)}}+v_{p_i,l+1}~(l\in j)\} fi,j=max{fi1,j(1<<l)+vpi,l+1 (lj)}
其中 p p p 是第 i i i 个人的原编号。
再考虑 i ≤ k + p i\leq k+p ik+p 时,转移分两种情况:

  1. i i i 个人可以作为观众,考虑需满足的条件:记 c n t cnt cnt 为集合 j j j 中元素个数,那么 i ≤ c n t + k i\leq cnt + k icnt+k,转移方程为:
    f i , j = max ⁡ { f i − 1 , j + a i } f_{i,j}=\max\{f_{{i-1},j}+a_i\} fi,j=max{fi1,j+ai}
  2. i i i 个人为运动员,转移方程:
    f i , j = max ⁡ { f i − 1 , j − ( 1 < < l ) + v p i , l + 1   ( l ∈ j ) } f_{i,j}=\max\{f_{{i-1,j -(1<<l)}}+v_{p_i,l+1}~(l\in j)\} fi,j=max{fi1,j(1<<l)+vpi,l+1 (lj)}

代码如下

#include <bits/stdc++.h>
#define N 100005
#define LL long long
using namespace std;
struct node{
	int i, v;
	bool operator < (const node & A) const{
		return v > A.v;
	}
}d[N];
LL z = 1, f[N][(1 << 7)];
int v[N][8];
int count(int x){
	int s = 0;
	while(x){
		if(x & 1) s++;
		x >>= 1;
	}
	return s;
}
int main(){
	int i, j, t, l, n, m, p, k, cnt;
	scanf("%d%d%d", &n, &p, &k);
	for(i = 1; i <= n; i++){
		scanf("%d", &d[i].v);
		d[i].i = i;
	}
	sort(d + 1, d + i);
	for(i = 1; i <= n; i++){
		for(j = 1; j <= p; j++) scanf("%d", &v[i][j]);
	}
	memset(f, 0xc0, sizeof(f));
	f[0][0] = 0;
	for(i = 1; i <= n; i++){
		for(j = 0; j < (1 << p); j++){
			cnt = count(j);
			f[i][j] = f[i - 1][j];
			if(i <= p + k && i - cnt <= k) f[i][j] = max(f[i][j], f[i - 1][j] + d[i].v);
			for(l = 0; l < p; l++){
				if(1 << l & j) f[i][j] = max(f[i][j], f[i - 1][j ^ (1 << l)] + v[d[i].i][l + 1]);
			}
		}
	}
	printf("%lld", f[n][(1 << p) - 1]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值