Millionaire 2008APAC local onsites C

/*Millionaire 2008APAC local onsites C
题意:最开始你有X元钱,要进行M轮赌博。
每一轮赢的概率为P,你可以选择赌与不赌,
如果赌也可以将所持的任意一部分钱作为赌注
(可以是整数,也可以是小数)。如果赢了,赌注将翻倍;
输了赌注则没了。在M轮赌博结束后,如果你持有的钱在100万元以上,
就可以把这些钱带回家。问:
当你采取最优策略时,
获得100万元以上的钱并带回家的概率是多少。
限制:P∈[0,1]
X∈[1,1000000]
M∈[1,15]
EG:输入 M=1 P=0.5 X=50000
输出 0.50000 (保留5位小数)
  解:
  分析:由于每一轮的赌注是任意的,不一定为整数,因而有无限种可能
  穷竭搜索是无法实现的。
  但细细的想一下 >>>每个过程采取最优策略  
  在最后一次的赌注时 你有钱数为x
  如果钱数x>100万,则没有必要再赌了即赢的概率为1;
  如果50<= x < 100万,只要参与赌博并且赌注>= 50万则有赢的概率为P;
  如果x< 50万,那么无论是否参与最后一轮的赌博,压的赌注是多少赢的概率必为0。
  图像如下:
  ^概率
  |                                          值
  |
  |                         ___________       1
  |                         .
  |           ______________.                 p
  |           .             .
  |           .             .
  |           .             .
  |           .             .
  0|0_________50万_________100万_______》金额 0
  最后一轮带钱回家的概率
  考虑最后二轮  同理有5种情况
  ^概率                                        值
  1|                          _________        1
  |                          .
  |                  ________.                 p
  |                  .       .
  |            ______.       .                 (p+p*p)/2
  |            .     .       .
  |     _______.     .       .                 p*p
  |     .      .     .       . 
  |     .      .     .       .      
  0|0___25____50万___75万___100万_______》金额  0
  最后两轮带钱回家的概率
  综上,当参与M轮赌博时所需考虑的情况总共有2^m + 1种
  动态规划: 枚举每种情况的期望,找到最优步骤即可
  
*/
# include <stdio.h>
# define QQ 15//最大值
# define max(a,b)((a)>(b)?(a):(b))
# define min(a,b)((a)<(b)?(a):(b))
double DP[2][1<<QQ+1]={0};//因为下一轮的概率只和当前轮的状态有关 所以只定义两行  用DP数组滚动
int main(){
    int m,w,x;
	int i,j,k,n,Flag[2]={0,1};
	double p=0,UU=0;    //1<<M+1=2^M+1=32768
    scanf("%d %lf %d",&m,&p,&x);
	n=1<<m;//n保存2^M 即2^M+1个DP位置
	DP[0][n]=1.0;//第n个位置对应资金大于1000000的概率
    for(i=0;i<m;i++)//共m轮循环
	{
	   for(j=0;j<=n;j++)
	   {
	       w=min(j,n-j);
           UU-=UU;//将UU变为0
		   for(k=0;k<=w;k++)
			   UU=max(UU,p*DP[Flag[0]][j+k]+(1-p)*DP[Flag[0]][j-k]);//UU存储进行下一轮赌博的最大概率
		 DP[Flag[1]][j]=UU;
	   }      //Flag[0]记录的永远是当前状态
      Flag[0]=1-Flag[0];//Flag[0]=0时 改为1  否则为1时改为0
	  Flag[1]=1-Flag[1];//用Flag[0]-[1]变换  降低空间复杂度
	}
	printf("%.5lf",DP[Flag[0]][(__int64)x*n/1000000]);
	return 0;
}

附加小程序:

/*
有两组数:A[N],B[N];
允许任意交换A B中各自的数字顺序
计算 A1*B1 +A2*B2 +A3*B3 +A4*B4 +……的最小值
限制条件:N∈[1,800]
Ai,Bi∈[-100000,100000]
解:
1:证明:当A B数组反序排列时 得到最小值
即 若A升序排列 则B降序排列  此时会得到最小值
先考虑初始情况:
N=2时
A[2]={A1,A2};A数组此时已经按照升序排列即A1<A2
B[2]={B1,B2};
比较 A1B1+A2B2 与 A1B2+A2B1 的大小关系(做差)
A1B1+A2B2 -( A1B2+A2B1)=(A1-A2)(B1-B2);
A1-A2<0  若此时B1>B2 结果取得最小值

  当N>2时
  如果B不是按降序排列
  即存在 i j 使得 Bi<Bj
  则交换Bi,Bj就取得更小的内积
  因此 当A B数组反序排列时 得到最小值
  可以快速排序 或选择排序
  但是 用int存结果会超界 int最大值为2147483647 因此需要用64位整int存储
下面代码中用随机数代替输入
你只需要输入N
*/
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# define TT 100000//区间
# define N 800//n的max
void paixu(int a[],int n,int b);//对A中的N个数排序 b=0升序 b=1降序
int main(){
	int A[N]={0},B[N]={0},n,i;
	__int64 sum=0;
    srand(time(NULL));
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
	  A[i]=-10000+rand()%(2*TT);
	  B[i]=-10000+rand()%(2*TT);
	  printf("A[%3d]=%5d,B[%3d]=%5d\n",i,A[i],i,B[i]);
	}
	paixu(A,n,0);
	paixu(B,n,1);
    for(i=0;i<n;i++)
		sum+=A[i]*B[i];
	printf("%I64d\n",sum);
    return 0;
}
void paixu(int a[],int n,int b)
{
    int i,j,k,temp;
	for(i=0;i<n-1;i++)
	{
		k=i;
		for(j=i+1;j<n;j++)
			if(b?a[j]>a[k]:a[j]<a[k])   //b=0升序  b为其他降序
				k=j;
			if(k!=i)
			{
				temp=a[k];
				a[k]=a[i];
				a[i]=temp;
			}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值