从
n
n
n个人里选
p
p
p人参加比赛, 选
k
k
k人当观众
(
n
≥
p
+
k
)
(n\geq p+k)
(n≥p+k)
第
i
i
i人当观众可以给答案带来贡献
a
i
a_i
ai
第
i
i
i人第
j
j
j个参赛可以给答案带来贡献
m
a
t
[
i
]
[
j
]
mat[i][j]
mat[i][j]
现给定如上数据, 求贡献最大
数据范围:
2
≤
n
≤
1
0
5
,
1
≤
q
≤
7
,
1
≤
k
,
k
+
p
≤
n
2\leq n\leq 10^5, 1\leq q \leq 7, 1\leq k, k + p\leq n
2≤n≤105,1≤q≤7,1≤k,k+p≤n
前置技能
状压
d
p
dp
dp
Tutorial:
要是没有k的限制就是个裸的状压, 先贪心的考虑
a
[
i
]
a[i]
a[i]最大的前
k
+
q
k+q
k+q个, 其中每个元素有三个选择, 要么当运动员, 要么当观众, 要么啥都不干, 可以看出来, 如果
a
[
i
]
<
a
[
j
]
a[i] < a[j]
a[i]<a[j], 那么无论如何, a[j]都不可能啥都不干, 所以我们可以对
a
[
i
]
a[i]
a[i]排个序, 然后状压转移, 由于k的限制, 当观众人数超过k的时候, 就不能贡献了, 所以转移方程肯定是由当前人数决定的
如何求目前观众的人数呢, i - mask(bits), 目前考虑到的人减去目前当运动员的人, 那么状态方程就很好想了:
{
d
p
[
i
]
[
n
e
w
]
=
m
a
x
(
d
p
[
i
]
[
n
e
w
]
,
d
p
[
i
−
1
]
[
o
l
d
]
+
m
a
t
[
x
]
[
e
+
1
]
)
d
p
[
i
]
[
c
u
r
]
=
m
a
x
(
d
p
[
i
−
1
]
[
c
u
r
]
+
a
[
x
]
,
d
p
[
i
]
[
c
u
r
]
)
第
i
人
可
以
当
观
众
d
p
[
i
]
[
c
u
r
]
=
m
a
x
(
d
p
[
i
]
[
c
u
r
]
,
d
p
[
i
−
1
]
[
c
u
r
]
)
;
第
i
人
不
能
当
观
众
\begin{dcases} dp[i][new] = max(dp[i][new], dp[i-1][old] + mat[x][e + 1]) \\ dp[i][cur] = max(dp[i - 1][cur] + a[x], dp[i][cur]) 第i人可以当观众\\ dp[i][cur] = max(dp[i][cur], dp[i - 1][cur]); 第i人不能当观众 \end{dcases}
⎩⎪⎨⎪⎧dp[i][new]=max(dp[i][new],dp[i−1][old]+mat[x][e+1])dp[i][cur]=max(dp[i−1][cur]+a[x],dp[i][cur])第i人可以当观众dp[i][cur]=max(dp[i][cur],dp[i−1][cur]);第i人不能当观众
具体步骤:
s
o
r
t
sort
sort使得
a
a
a降序
构造状态
d
p
[
i
]
[
m
a
s
k
]
dp[i][mask]
dp[i][mask]表示从
1
,
2
,
3
,
⋯
i
1, 2, 3 , \dotsm i
1,2,3,⋯i里选择状态为
m
a
s
k
mask
mask的最大收益
转移状态
复杂度:
O
(
n
×
2
p
+
n
l
o
g
(
n
)
)
O(n\times 2^p + nlog(n))
O(n×2p+nlog(n))
code:
#include<bits/stdc++.h>#include<bits/extc++.h>usingnamespace std;#define _rep(n, a, b) for (ll n = (a); n <= (b); ++n)#define _rev(n, a, b) for (ll n = (a); n >= (b); --n)#define _for(n, a, b) for (ll n = (a); n < (b); ++n)#define _rof(n, a, b) for (ll n = (a); n > (b); --n)#define oo 0x3f3f3f3f3f3fll#define ll long long#define db double#define eps 1e-8#define bin(x) cout << bitset<10>(x) << endl;#define what_is(x) cerr << #x << " is " << x << endl#define met(a, b) memset(a, b, sizeof(a))#define all(x) x.begin(), x.end()#define pii pair<ll, ll>#define pdd pair<db, db>#define pi acos(-1.0)#define lowbit(x) x&(-x)const ll maxn =1e5+10;const ll mod =1e9;
ll dp[maxn][1<<8];int n, p, k;
ll a[maxn], id[maxn];
ll mat[maxn][8];signedmain(){
cin >> n >> p >> k;_rep(i,1, n) cin >> a[i];_rep(i,1, n) id[i]= i;sort(id +1, id +1+ n,[](ll& x, ll& y){return a[x]> a[y];});_rep(i,1, n){_rep(j,1, p){
cin >> mat[i][j];}}_rep(i,1, n){
ll x = id[i];_rep(cur,0,(1<< p)-1){
ll has =__builtin_popcount(cur);
has = i - has;if(has <=0)continue;if(has <= k)//第x人要么是观众要么选他{
dp[i][cur]=max(dp[i -1][cur]+ a[x], dp[i][cur]);_for(e,0, p){if(!((cur >> e)&1))//第i位是0{
dp[i][cur ^(1<< e)]=max(dp[i][cur ^(1<< e)], dp[i -1][cur]+ mat[x][e +1]);}}}else{
dp[i][cur]=max(dp[i][cur], dp[i -1][cur]);_for(e,0, p){if(!((cur >> e)&1))//第i位是0{
dp[i][cur ^(1<< e)]=max(dp[i][cur ^(1<< e)], dp[i -1][cur]+ mat[x][e +1]);}}}}}
cout << dp[n][(1<< p)-1]<< endl;}
E. team BuildingFace题意从nnn个人里选ppp人参加比赛, 选kkk人当观众(n≥p+k)(n\geq p+k)(n≥p+k)第iii人当观众可以给答案带来贡献aia_iai第iii人第jjj个参赛可以给答案带来贡献mat[i][j]mat[i][j]mat[i][j]现给定如上数据, 求贡献最大数据范围: 2≤n≤105,1≤q≤7,1≤k,k+p≤n...