Google kick start 2018 Round A - Lucky Dip

题目链接

这个题当时就思路错掉了,样例都推不出来,看来想进google数学得过关。

题意就是给你一堆有权值的球,每次等概率拿一个,可以放回,有K次机会,求最大期望。其实我当时根本没在意最大期望和那句“如果你第K次拿了,那你就只能留在手里了”,看了官方分析才知道,这两句话有大用,不然就是求期望,而不是最大期望,吃了没文化的亏。

如果我没有选择权的时候,我只能拿到啥就算啥,于是乎K = 0的时候我能拿到的最大期望就是E[0]=\frac{\sum_{i = 0}^{n}num[i]}{n}

如果我有选择权,那就是另一个故事了。我如果有一次选择权我会怎么办,我就拿15 11 7 4 1这个样例来说吧。如果我手上本来就是15 11这样的(>E[0] = 7.8),我明显不用再丢回去铤而走险吧,因为丢回去了就一波定乾坤了,万一拿了个3乌龙了咋整。于是乎,我会选择,如果我第一次拿了3 5 7这样的比E[0]低的我才会把求丢回去再检验一波人品。这样我能拿到的期望就不一样了,我第一次拿了15和11,那我就不去玩下一波了,如果我拿了7 4 1(气死你?)那我就又获得了一次拿重新抽奖的机会,能抽到啥我不知道,但是期望值是E[0]没假。所以E[1]=\frac{\sum_{i = 0}^{n}max(num[i],E[0])}{n},后面就是依次类推了。如果你非要求概率那也是可以的,我会附上求概率的代码。

思路是这样:第一次拿球有一个期望E[0] =\frac{\sum Vi}{N} ,后面每一个放回拿球的期望E[i]都是要和前一次的期望做对比的,因为如果我第二次拿球拿的值还不如上一次的期望值高那我还拿干什么,所以E[i] = \frac{\sum min(Vi,E[i-1]]))}{n}。说实话,如果这个推不出来这个题小样例都过不去。太南了。


#include<bits/stdc++.h>
using namespace std;
double num[2*10240];
double sum[2*10240];
double E[5*10240];
int main()
{
    int testcase;
    cin >> testcase;
    for(int kase = 1;kase<=testcase;kase++)
    {
    	memset(num,0,sizeof(num));
    	memset(E,0,sizeof(E));
    	int n,K;
    	cin >> n >> K;
    	for(int i = 0;i < n;i++)
    		cin >> num[i];
		sort(num,num + n);
		sum[0] = num[0];
		for(int i = 1;i < n;i++)
    		sum[i] = num[i] + sum[i-1];
    	for(int i = 0;i < n;i++)
    		E[0] += num[i];
    	E[0] /= n;
    	for(int i = 1;i <= K;i++)
    	{
    		int ind = lower_bound(num,num+n,E[i-1]) - num;
    		E[i] += ind * E[i-1];
    		//cout << E[i] << endl; 
    		E[i] += sum[n-1]-sum[ind-1];
    		//cout << E[i] << endl;
    		E[i] /= n;
    		//cout << E[i] << endl; 
		}
    	printf("Case #%d: %.6lf\n",kase,E[K]);
	}
    return 0;
}

求概率,思路和上面一样,就是算了一下概率,比如说我要是第一次拿每个的概率都是1/n,如果我又一次重拿的机会,那我只需要把需要放回的那些再求一波概率。

#include<bits/stdc++.h>
using namespace std;
double num[2*10240];
double sum[2*10240];
double p[2*10240];
double E[5*10240];
int main()
{
    int testcase;
    cin >> testcase;
    for(int kase = 1;kase<=testcase;kase++)
    {
    	memset(num,0,sizeof(num));
    	memset(E,0,sizeof(E));
    	int n,K;
    	cin >> n >> K;
    	for(int i = 0;i < n;i++){ 
    		cin >> num[i];
    		p[i] = 1.0/n; 
    		} 
    	double P = 1.0/n;
		sort(num,num + n);
		sum[0] = num[0];
		for(int i = 1;i < n;i++)
    		sum[i] = num[i] + sum[i-1];
    	for(int i = 0;i < n;i++)
    		E[0] += num[i];
    	E[0] /= n;
    	for(int i = 1;i <= K;i++)
    	{
    		int ind = lower_bound(num,num+n,E[i-1]) - num;
    		double tmp = 0;
    		for(int j = 0;j < ind;j++)
    		{
    			p[j] *= ind*1.0/n;
    			tmp += p[j];
    			E[i] += p[j] *num[j];
			}
			tmp = 1-tmp;
			tmp /= (n-ind);
			for(int j = ind;j < n;j++)
			{
				p[j] = tmp;
				E[i] += p[j]*num[j];
			}
		}
    	printf("Case #%d: %.6lf\n",kase,E[K]);
	}
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值