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;
}
posted @ 2024-07-03 16:38  Showball  阅读(87)  评论(0编辑  收藏  举报