车牌摇号 程序设计题

来源:互联网 发布:娜迦皇家卫兵知乎 编辑:程序博客网 时间:2024/04/28 13:28
阿里面试题(车牌摇号问题)
题目大意:由于北京车牌紧张,如果需要车牌,需要在每月中旬进行摇号来获得车牌,获得车牌的概率由下面式子决定 money/100000. (money就是摇号时支付的预约金),如果摇中,与预约金用来买车牌,如果没有摇中,这预约金退回。如果你希望在6个月中摇到车牌,问在规定的时间摇到车牌,最少的期望金额是多少?

对于这个问题,可以直接用数学公式进行求解,可以列出如下计算公式:
(x1 * x1/100000)+ ( 1 - x1/100000) * (x2 * x2 /100000) + (1 - x1/100000) * (1 - x2/100000) * (x3 * x3 / 100000) + (1 - x1/100000) * (1 - x2/100000) *(1 - x3/100000)*(x4* x4 / 100000) + (1 - x1/100000) * (1 - x2/100000) * (1 - x3/100000)*(1 - x4/100000)*(x5* x5 / 100000) +(1 - x1/100000) * (1 - x2/100000) * (1 - x3/100000)*(1 - x4/100000)*(1 - x5/100000)*(100000) 

对于求解上式,可以通过SGD (随机梯度下降法进行求解),代码如下:
#include <iostream>#include <stdio.h>#include <vector>#include <map>#include <cmath>#include <time.h>#include <random>#include <set>#include <queue>#include <algorithm>using namespace std;#define inf 100000000// ctrl + k and then press c 注释// ctrl + k and then press u 取消注释// ctrl + shift + L 删除当前行// 用SGD求解 如下函数// f(x0,x1,x2,x3,x4,x5) = x1 * x1 / money + (1 - x1/money) * x2 * x2 / money + (1 - x1/money) * (1 - x2/money) * x3 * x3 / money + ... + (1 - x1/money) * ...*(1 - x4/money) * x5 * x5 / moneydouble lamba,money;double x[6];// 控制变量值的范围void Update(double& a,double gradient,double lamba){//printf("a = %lf and gradient = %lf and lamba = %lf\n",a,gradient,lamba);double result = a - gradient * lamba;if(result >= 0 && result <= money)a = result;}// (1 - x1/money) * (1 - x2/money) * (1 - x3/money) * (1 - x4/money) * ...*(1 - xn/money)// excp 表示不乘以(1 - Xexcp/money)double cal_1_to_n(int n,int excp){double tmp = 1.0;for(int i = 0;i<=n;i++)if(i != excp){tmp *= (1 - x[i] / money);}return tmp;}int main(){double steps;printf("Please Input money = ");scanf("%lf",&money);  //在题目中就100000printf("Please Input steps = ");scanf("%lf",&steps); // 随机梯度迭代次数printf("Please Input lamba = ");scanf("%lf",&lamba);  //梯度下降时的步长srand((unsigned)time(NULL));   // 随机种子// 变量随机值for(int i = 0 ;i< 6;i++)x[i] = rand() % (int(money) + 1);x[5] = money;double gradient;for(int i = 0;i<steps;i++){for(int j = 0;j<5;j++){// 计算梯度值gradient = 2.0 * x[j] / money;gradient *= cal_1_to_n(j - 1,10);double tmp = 1.0;for(int k = j + 1;k<=5;k++){tmp = cal_1_to_n(k-1,j);tmp *= x[k] * x[k] / money;tmp /= money;gradient -= tmp;}/*printf("steps = %d\n",i);printf("x[%d] gradient is %lf\n",j,gradient);*/// 更新梯度值Update(x[j],gradient,lamba);}double min_val = 0.0;// 计算期望值for(int i = 0;i<6;i++){min_val += cal_1_to_n(i-1,10) * x[i] * x[i] / money;}//每迭代100次 打印输出if(i % 100 == 0){for(int i = 0;i<6;i++)printf("x[%d] =  %lf ",i,x[i]);printf("\n");printf("the min_val is %lf\n",min_val);}}return 0;}

除了这种通过数学求解,我们也可以通过动态规划的方式进行求解。如果我们考虑所有情况,时间复杂度太大,不过我们可以缩减动态规划的空间,让结果在可控范围内就行,代码如下:
#include <iostream>#include <stdio.h>#include <vector>#include <map>#include <cmath>#include <set>#include <queue>#include <algorithm>using namespace std;#define inf 100000000// ctrl + k and then press c 注释// ctrl + k and then press u 取消注释// ctrl + shift + L 删除当前行int main(){    int precision,money;    printf("Please Input precision = ");    scanf("%d",&precision);    printf("Please Input money = ");    scanf("%d",&money);    double **dp = new double* [6];    for(int i = 0;i<6;i++)        dp[i] = new double[precision + 1];    //dp[i][j] 表示第i轮过后,在没有拿到拍照的概率为j的情况下,花费最少的钱。    for(int i = 0;i<6;i++)        for(int j = 0;j<=precision;j++)            dp[i][j] = inf;    dp[0][precision] = 0;    for(int i = 0; i < 5;i++)    {        for(int j = 0;j<=precision;j++)        {            if(dp[i][j] < inf - 1)            {                for(int k = 0;k<=money;k++)                {                    int no_card_rate = (int)(j - k * 1.0 * j/ money); // 对概率这一维进行压缩,只保留1/precision 的精度。                    dp[i+1][no_card_rate] = min(dp[i+1][no_card_rate],dp[i][j] + (k * 1.0 * k * j /precision/ money));                }            }        }    }    double Min_val = inf;    for(int i = 0;i<=precision;i++)    {        Min_val = min(Min_val,dp[5][i] + (i * 1.0/precision) * money);    }    cout << Min_val << endl;    return 0;}

结论:如果必须在6个月之内拿到拍照,期望金额是 100000 * 40%。可以思考,最后的期望金额只跟时间有关,跟100000没什么关系。


 
0 0
原创粉丝点击