/*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;
}
}
}