2018-03-08 18:02:20
A - A Dangerous Maze LightOJ - 1027
题意: 小花的迷宫入口有n个门 走每个门的概率都是相同的 每个门有一个值x x可正可负 正代表x 分钟后可以征服迷宫 负代表|x|分钟之后小花会回到原点 问征服迷宫时间的期望
题解:假设小花平均E分钟可以征服迷宫
如果小花刚开始选择了一个值为负的门 那么此时的时间变成了E+|Xi| 概率为1/n 因为选择每个门的概率都相同 PS:相当于在原来可以出去的基础上浪费了|Xi|分钟
如果小花刚开始选择了一个值为正的门 那么此时的时间变成了Xi 概率为1/n 因为选择每个门的概率都相同
假设值为正的门有n0个 值为负的门有n1个 正的门的值的总和为T0 负的门的值的总和为|T1|
则 E = T0/n + (E+|Xi|)/n*n1
= T0/n + |T1|/n + E/n*n1
n0/n*E = (T0+|T1|)/n
E = (T0 + |T1|) / n0
刚接触概率与期望 感谢http://blog.csdn.net/jinglinxiao/article/details/70226889
代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int t;
int n , a[110];
int num , tmp , sum;
int gcd(int a , int b)//最大公因数
{
return b==0?a:gcd(b , a%b);
}
int main()
{
scanf("%d" , &t);
for(int cas=1; cas<=t; cas++)
{
scanf("%d" , &n);
num = 0;
sum = 0;
for(int i=0; i<n; i++)
{
scanf("%d" , &tmp);
sum += abs(tmp);
if(tmp < 0) continue;
num++;
}
if(num == 0)
{
printf("Case %d: inf\n" , cas);
continue;
}
tmp = gcd(sum , num);
printf("Case %d: %d/%d\n" , cas , sum/tmp , num/tmp);
}
return 0;
}
B - Discovering Gold LightOJ - 1030
题意: 小花进入到了一个1*n的山洞 n个单元格标号为1~n 刚开始小花在1号点 你可以想象为在玩飞行棋 走之前需要先掷骰子 掷出来的数字是几就走几步 如果超过了山洞的长度就重新掷 每个单元格上都有一定数量 对的黄金 问能够得到黄金的期望
题解: 这道题我是先求出了到达每个点的概率 然后将每个单元格的概率与黄金数量相乘将所有单元格相加 得到结果 相当于是加权平均值 概率的求法就是1号点的概率为1 之后他能够到达的点(不超过n)的概率相同 为1/x*(前继的概率) x为最多能掷出来的数字
代码如下:
#include<stdio.h>
#include<iostream>
#include<queue>
//#include<algorithm>
#include<string.h>
using namespace std;
queue<int>Q;
int t , n , a[110];
double b[110];
void init()
{
while( !Q.empty() )
Q.pop();
memset(b , 0 , sizeof(b));
}
void input()
{
for(int i=1; i<=n; i++)
{
scanf("%d" , &a[i]);
}
}
void solve()
{
b[1] = 1.00;
for(int i=1; i<=n; i++)
{
int j = 1;
for( ; j<=6; j++)
{
if(i+j>n)
break;
}
j--;
for(int k=1; k<=j; k++)
{
b[i+k] += 1.00*b[i]/j;
}
}
double sum = 0.00;
for(int i=1; i<=n; i++)
{
sum += a[i]*b[i];
}
printf("%.8lf\n" , sum);
}
int main()
{
scanf("%d" , &t);
for(int cas=1; cas<=t; cas++)
{
scanf("%d" , &n);
init();
input();
printf("Case %d: " , cas);
if(n == 1)
printf("%d\n" , a[1]);
else if(n == 2)
printf("%d\n" , a[1]+a[n]);
else
solve();
}
return 0
2018-03-09 14:26:3
题意: 小花是个聪明的孩子 给一个数字n 将这个数字除以其中一个因子 得到新的n 一直除下去 直到得到1 问除的次数的期望
题解: 每个数字除的次数可以根据他所有的因子得到 包括1与本身 设d[]表示每个数字除至1的次数的期望(一下简称次数) 那么一个数字的次数等于因子的次数加1 因为需要先除一下到因子 这里的因子指所有因子 假设有c个 那么取到每个因子的概率均为1/c 那么通过这个因子到1的次数的期望为d[因子]/c 假设i1 i2 i3...ic-1 ic 分别代表数字i的c个因子 那么可以得到 d[i] = 1+(d[i1]+d[i2]+...+d[ic-1]+d[ic])/c 化简可得 d[i] = (d[i1]+d[i2]+...+d[ic-1])+c/(c-1) 上面是不包括本身的所有因子的次数的期望 数字为1e5 经过一次预处理 每次输入可以O(1)的输出
代码如下:
#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
int t , n;
double d[100010];
int num;
double sum;
double tmp;
void init()
{
d[1] = 0.00;
for(int i=2; i<=100000; i++)
{
num = 0;
sum = 0.0;
tmp = sqrt(i);
num += 2;// 1与本身
// sum += d[1];//d[1]=0 可以省略
for(int j=2; j<tmp; j++)
{
if(i%j != 0) continue;
num += 2;
sum += d[j];
sum += d[i/j];
}
if(tmp == (int)tmp)
{
num++;
sum += d[(int)tmp];
}
// printf("%d...%.8lf\n" , num , sum);
d[i] = (sum+num)/(num-1);
}
}
int main()
{
scanf("%d" , &t);
init();
for(int cas=1; cas<=t; cas++)
{
scanf("%d" , &n);
printf("Case %d: %.8lf\n" , cas , d[n]);
}
return 0;
}
D - Just another Robbery LightOJ - 1079
2018-05-06 15:37:51
题意:小花闲的没事要去抢银行 她的队友拼命拦住她说:“蚂蚁竞走了十年了,你冷静一点!” 但是我行故我在 我执意要翘课去抢劫 内心戏怎么这么多 小花的危险值的上限是P 一个分数 有n个银行 每个银行有v[i]元钱 抢劫的时候被抓住的概率是p[i] 问在不被抓住的情况下 最多可以抢劫多少钱
题解:这道题看到之后就知道是一道裸的 0 1 背包的题 每个银行有抢或不抢两种情况 不加空间优化的 01 背包 dp[i][j]在背包中代表装了i件物品 占据了j的体积得到的价值 这道题中dp[i][j]代表的是 抢了i家银行 得到j元钱 被抓到的概率 最后求n个银行都处理完之后 满足概率要求的最大的j 其实这个题刚开始看不会写 因为刚开始会把得到的钱当做价值 概率当做体积 这样的话 真是不知道怎么写 因为概率是小数啊 01 背包没有教我怎么遍历啊 于是转变想法就好 空间压缩的改一下就好
代码如下:
#include<stdio.h>
#include<iostream>
using namespace std;
int t;
int n;
double P;
int v[110];
double p[110];
double dp[105][105*105];
int sum;
void init()
{
sum = 0;
}
void input()
{
for(int i=1; i<=n; i++)
{
scanf("%d%lf" , &v[i] , &p[i]);
sum += v[i];
}
}
void solve()
{
/*
dp[i][j] 记录的是抢了i个银行 得到了j元钱 被抓的概率
*/
for(int i=1; i<=sum; i++)
dp[0][i] = -1; //抢了0个银行 得到j元钱是不可能的 j>0
dp[0][0] = 0; //抢了0个银行 得到0元钱 被抓的概率是0
for(int i=1; i<=n; i++)
{
for(int j=0; j<=sum; j++)
{
/*
因为dp初始值都为0 所以只有在i-1=0的时候才会出现小于-0.5 / <0的情况
*/
if(j<v[i] || dp[i-1][j-v[i]]<=-1) //第i个银行强不了(j<v[i]) 或者虽然第i个银行可以抢 但是如果抢了他 他前面的九不成立了 也就是虽然可以建二楼 但是你一楼就垮了 所以二楼还是一楼
dp[i][j] = dp[i-1][j];
else if(dp[i-1][j]<0)
dp[i][j] = dp[i-1][j-v[i]] + (1-dp[i-1][j-v[i]])*p[i]; //第i个银行可以抢 因为j>v[i] if里对应抢i-1个银行得不到j元钱的情况 此时第i个银行一定得抢
else
dp[i][j] = min(dp[i-1][j], dp[i-1][j-v[i]]+(1-dp[i-1][j-v[i]])*p[i]); //前i-1次被抓的概率是dp[i-1][j-v[i]] 于是不被抓的概率就是1-dp[i-1][j-v[i]] 再乘上此时被抓的概率
}
}
}
int main()
{
scanf("%d" , &t);
for(int cas=1; cas<=t; cas++)
{
scanf("%lf%d" , &P , &n);
init();
input();
solve();
int ans = 0;
// for(int j=0; j<=n; j++)
// {
for(int i=0; i<=sum; i++)
{
if(dp[n][i]>-1 && dp[n][i]<P)
ans = i;
// printf("%lf..." , dp[j][i]);
}
// printf("\n");
// }
printf("Case %d: %d\n" , cas , ans);
}
return 0;
}
E - Birthday Paradox LightOJ - 1104
题意: 给出一年的天数 不一定是365天 你要邀请朋友到你家做客 问最少邀请多少人会使得存在两个人生日相同的概率大于0.5
题解:本小花想开一个二维dp数组 dp[i][0] 表示请i个人 所有人生日都不同的概率 dp[i][1]代表请i个人 至少有两个人生日相同的概率 后来发现 dp[i][1]不就等于1-dp[i][0]么 于是直接求dp[i][0] 一个人的时候为1 因为不可能出现生日相同 之后每加一个人就是乘上(n-i+1)/n
疑问点:惹不起的出题人系列 我考虑了一下 输入为1 和 2的边界情况 发现输出的是1 于是心想 一个人怎么会出现两个人的生日概率相同啊 于是暗自得意 这就是这个题的坑点吧 自信满满的在n==1||n==2的时候特判输出了2 哈哈 不知道为什么WA了 把自以为对的特判删掉就对了 谁来告诉我一下 一个人的时候怎么有两个人的生日相同的概率呢。。 恰逢今天山东省赛 F题暴力1e42能过 让我又想起了去年青岛的区域赛暴力 于是今天真的惹不起出题人了
代码如下:
#include<stdio.h>
#include<iostream>
using namespace std;
int t;
int n;
double dp[100005];
int main()
{
// printf("%lf.....%lf...\n" , 1.00*364/(365*365) , (1.00+364*3.0)/(365*365*365));
scanf("%d" , &t);
for(int cas=1; cas<=t; cas++)
{
scanf("%d" , &n);
// if(n <= 2)
// {
// printf("Case %d: 2\n" , cas);
// continue;
// }
dp[1] = 1;
// dp[1][1] = 0;
int i;
for(i=2; i<=100000; i++)
{
dp[i] = dp[i-1] * (1.00*(n-i+1)/n);
// dp[i][1] = dp[i-1][1] + dp[i-1][0]*(1.00*(i-1)/n);
// printf("%d.....%lf........\n" , i , 1-dp[i]);
if((1-dp[i]) >= 0.5)
break;
}
printf("Case %d: %d\n" , cas , i-1);
}
return 0;
}
G - Dice (III) LightOJ - 1248
题意: 小花掷硬币 火星硬币有n个面 要求每个面都出现过时的最少投掷次数的期望
思路: dp[i]记录得到i个面投掷次数的期望 我们可以知道dp[1]=1 因为投掷一次只能得到一个面 得到一个面投掷一次就够了
状态转移方程可以参考A题 我要得到第i个面的时候 我有 i-1/n 的概率得到和之前一样的面 有 (n-(i-1))/n的概率在dp[i-1]的基础上得到第 i 个面
于是 dp[i] = dp[i-1]* (n-(i-1))/n +1 + dp[i]* (i-1)/n 为什么要加1呢 因为我们求的是次数的期望 得到一个新的面自然要加1
移项化简得 dp[i] = dp[i-1]+n/(n-i+1)
代码如下:
#include<stdio.h>
#include<iostream>
using namespace std;
int t , n;
double dp[100005];
int main()
{
scanf("%d" , &t);
for(int cas=1; cas<=t; cas++)
{
scanf("%d" , &n);
dp[1] = 1.00;
for(int i=2; i<=n; i++)
{
dp[i] = dp[i-1]+1.00*n/(n-(i-1));
}
printf("Case %d: %.15f\n" , cas , dp[n]);
}
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4381733/blog/4055177