P7137-[THUPC2021 初赛]切切糕【dp】
正题
题目链接:https://www.luogu.com.cn/problem/P7137
题目大意
有两个人,有\(n\)个蛋糕,第\(i\)个蛋糕大小为\(a_i\)。
每一次第一个人可以选择一个蛋糕把它切成任意大小的两份(一份可以为空)。
然后第二个人有\(m\)次机会优先选择一份拿走,否则都是第一个人先拿。
两个人都希望自己拿走的最多。
求第一个人最终能拿走多少。
\(1\leq m\leq n\leq 2500\)
解题思路
现在看来还挺简单的,一雪前耻了属于是。
因为是自定义顺序,所以考虑起来比较麻烦所以我们先考虑怎么确定拿的顺序。
瞎猜感性思考一下不难发现,如果我们把大的放在前面,那么第一个人分的时候就分太不平均,因为第二个人手里有选择权威慑,但是如果我们把小的放在前面显然威慑权就到第一个人手里了。因为如果第二个交了太多次机会那么第一个人后面直接全拿,所以此时第二个人不敢交这么多次机会那第一个人就可以全拿了。
所以我们直接敲定是从小到大分,那么顺序固定之后就很简单了,反过来推,设\(f_{i,j}\)表示分到第\(i\)时还有\(j\)次选择权的第一个人总和。
那么我们就有转移方程。
\[f_{i,j}=max\{min\{f_{i-1,j-1}+a_i-x,f_{i-1,j}+x\}\}(x\in[0,\frac{m}{2}])
\]
考虑一下怎么确定这个\(x\),其实也很简单,设\(A=f_{i,j},B=f_{i,j-1}\),那么显然有\(B>A\)。
如果\(B-A\geq a_i\)那么我们直接全拿\(a_i\)这样第二个人也不会用机会,此时\(f_{i,j}=B\)。
如果\(B-A\leq a_i\)那么我们把\(a_i\)分给\(A\)不足\(B\)的那部分后剩下的平分给\(A\)和\(B\),此时\(f_{i,j}=B+\frac{a_i-(B-A)}{2}\)。
时间复杂度:\(O(nm)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2510;
int n,m;
double a[N],f[N][N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
sort(a+1,a+1+n);
for(int i=n;i>=1;i--){
f[i][0]=f[i+1][0]+a[i];
for(int j=1;j<=m;j++){
double A=f[i+1][j],B=f[i+1][j-1];
//B>A x<=a/2 B+x A+a-x
if(B-A<=a[i])
f[i][j]=B+(a[i]-B+A)/2.0;
else f[i][j]=A+a[i];
}
}
printf("%.6lf",f[1][m]);
return 0;
}