CF 1981 D. World is Mine (*1800) DP+博弈论
CF 1981 D. World is Mine (*1800) DP+博弈论
题意:
有 \(n\) 个蛋糕, 每个蛋糕有一个美味值 \(a_i\), \(Alice\) 和 \(Bob\) 轮流吃蛋糕, \(Alice\) 每次必须选择吃严格大于之前所吃的蛋糕美味程度。 \(Bob\) 随意选择。有人没有蛋糕可以吃时,游戏结束。 \(Alice\) 想吃更多蛋糕,\(Bob\) 想要阻止。
双方均采用最优策略,求 \(Alice\) 最多能吃多少蛋糕。
思路:
贪心的去思考,我们注意到 \(Alice\) 选择蛋糕的策略是固定的,每次只会选择剩余蛋糕中,大于之前蛋糕美味值的最小美味值的蛋糕。而对于 \(Bob\) 来说,他如果想要让 \(Alice\) 少吃一种蛋糕,就必须把这种美味值的蛋糕全部吃掉,否则没有意义。所以对于 \(Bob\) 来说,每种蛋糕要么不吃,要么就全部吃完。不妨考虑 \(DP\), 令 \(f_{i,j}\) 表示对于前 \(i\) 种蛋糕,后手吃完了其中的 \(j\) 个,至少需要多少次操作。那么我们如果取走的 \(j\) 种蛋糕共有 \(x\) 个。那么一定要满足 $x \le i - j $ 才可以,否则没法在Alice取之前全部取走。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define all(u) u.begin(), u.end()
#define endl '\n'
#define debug(x) cout<<#x<<":"<<x<<endl;
typedef pair<int, int> PII;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10, M = 105;
const int mod = 1e9 + 7;
const int cases = 1;
void Showball(){
int n;
cin>>n;
vector<int> cnt(n+1);
for(int i=0;i<n;i++){
int x;
cin>>x;
cnt[x]++;
}
vector<int> cand;
for(int i=1;i<=n;i++){
if(cnt[i]) cand.pb(cnt[i]);
}
int m=cand.size();
vector<int> f(m+1,inf);
f[0]=0;
for(int i=0;i<m;i++){
auto nf=f;
for(int j=0;j<i;j++){
if(f[j]+cand[i]<=i-j){
nf[j+1]=min(nf[j+1],f[j]+cand[i]);
}
}
f=nf;
}
int ans=inf;
for(int i=m;i>=0;i--) if(f[i]!=inf) return cout<<m-i<<endl,void();
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
if(cases) cin>>T;
while(T--)
Showball();
return 0;
}