Samara Farewell Contest 2020 (XXI Open Cup, GP of Samara) D. Two Pirates - 2
D. Two Pirates - 2
Two pirates are dividing looted treasures. There are n treasures, the i-th of which costs ai gold. They move in rotation, each turn a pirate picks one of the remaining treasures. The thing is, the second pirate is drunk, so he doesn't make optimal picks and each turn just picks a random available treasure, uniformly. The first pirate knows that and always makes optimal picks.
Find the expected costs of treasures picked by both pirates.
The first line contains an integer n (1≤n≤5000) — the number of treasures.
The second line contains n integers ai— the costs of the treasures.
Output two floating-point numbers: the expected costs of treasures picked by the first and the second pirates.
The absolute or relative error of the numbers shouldn't exceed 10−9.
2
1 3
3.000000000000000 1.000000000000000
3
2 1 4
5.500000000000000 1.500000000000000
题目描述:
有n个价值为a[i]的宝藏,清醒的海盗和喝醉的海盗轮流拿。清醒的海盗总是会拿价值最大的宝藏,喝醉的海盗则会随机挑一个剩下的,问两个海盗获得宝藏总价值的期望。
分析:(下面是官方题解)

大概的意思就是,先把宝藏价值从小到大排序,按正常来说,清醒海盗每次都拿最大价值的也就是最后一个,醉海盗则是随机拿。
现在把他反过来想,在空箱子中从小到大轮流放球,清醒海盗放的球为黑色,醉海盗为白色,第i个球为黑色表示清醒海盗拿了第i个宝藏。
dp[i][j]表示已经放了i个球,从左数第j个球为黑色的概率。
当清醒海盗放球时,转移为dp[i+1][j]=dp[i][j] (j<i+1)。dp[i+1][i+1]=1。因为清醒海盗必定选最后一个,也就是把黑球放在最右边。
当醉海盗放球时,dp[i+1][j]=(j-1)/(i+1) * dp[i][j-1] + (i+1-j)/(i+1) * dp[i][j],对应醉海盗把白球放左边时,会使原来在j-1位置的球到达j位置。把白球放右边时,j位置的球还是在j位置。
关于谁先放与宝藏数量的奇偶有关,当为奇数时,因为清醒海盗会多拿一个,所以肯定要先手。当为偶数时,因为先放拿到的价值最小,所以要醉海盗先放。
最后答案就是dp[n][i] * a[i]的总和。
代码:
#include<cstdio> #include<iostream> #include<deque> #include<cstring> #include<cmath> #include<map> #include<vector> #include<bits/stdc++.h> #define sd(x) scanf("%d",&x) #define lsd(x) scanf("%lld",&x) #define sf(x) scanf("%lf",&x) #define ms(x,y) memset(x,y,sizeof x) #define fu(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define all(a) a.begin(),a.end() #define range(a,x,y) a+x,a+y+x using namespace std; using namespace __gnu_cxx; typedef long long ll; typedef unsigned long long ull; typedef long double ld; typedef pair<char,int> P; const int N=5e3+99; ll mod=2147493647; const int INF=1e9+7; int a[N],n; double dp[N][N]; int main() { sd(n); fu(i,1,n) { sd(a[i]); } sort(a+1,a+n+1); bool fmove=n%2;//偶数个的话,醉海盗先放,奇数个的话清醒海盗先放 fu(i,1,n) { fu(j,1,n) { if(fmove) { //清醒海盗 if(i==j) dp[i][j]=1; else dp[i][j]=dp[i-1][j]; } else { //醉海盗 double lp=(j-1)*1.0/i,rp=(i-j)*1.0/i; dp[i][j]=dp[i-1][j-1]*lp+dp[i-1][j]*rp; } } fmove^=1; } double ans1=0,ans2=0; fu(i,1,n) { ans1+=dp[n][i]*a[i];//清醒海盗拿的 ans2+=(1-dp[n][i])*a[i];//醉海盗拿的 } printf("%.15f %.15f\n",ans1,ans2); return 0; }
浙公网安备 33010602011771号