【题目】
空间限制: 65536K
● 题目描述
在《流浪地球》电影中,地球上大部分的行星发动机被摧毁。
人类再一次展开全球性救援,现在告诉你每只救援队的目标发动机的编号以及这只救援队在成功救援的概率,假如有至少k个行星发动机能够得到重启,则认为地球会被拯救。
● 输入描述
第一行给出N,M,K。N代表人类派出的救援队总数,M代表被摧毁的行星发动机,K代表至少需要重启的行星发动机总数。(1<=N<=1e5,K<=M<=2000)
接下来N行,每行给出ai,pi,分别代表第i支救援队的目标发动机的编号是ai,救援成功的概率为pi。(1<=ai<=M,0<=pi<=1)
只要有一只救援队顺利抵达该行星发动机,则认为该发动机被成功重启。
● 输出描述
输出地球被救援成功的概率(请严格保留3位小数)
● 示例1
输入
3 2 2
1 1
1 1
2 0.5
输出
0.500
【思路】
称发动机被成功重启为“发动机成功”,否则为“发动机失败”。
P(地球被救援成功)=P(至少k台发动机成功)=i=kmP(有i台成功)
动态规划
设dp[i][j]为P(在i台中有j台成功),则
dp[i][j]=dp[i-1][j]*P(发动机i失败)+dp[i-1][j-1]* P(发动机i成功),
用自然语言描述就是
在i台中有j台成功的概率=在i-1台中有j台成功且i失败的概率+ 在i-1台中有j-1台成功且i成功的概率
dp[i][j]的值仅依赖于dp[i-1][j]和dp[i-1][j-1]
,因此以递增i的方式计算。
空间复杂度优化:
压缩至一维,则dp[i][j]=dp[i][j]*P(发动机i失败)+dp[i][j-1]* P(发动机i成功)
,此时需要以递减j的方式计算。
#include
int main(){
int n,m,k;//n代表救援队总数,m代表故障发动机总数,k代表最少需要重启的发动机数量
double s[2005]={0};//s[i]存储i号发动机成功概率
double ss[2005]={0};
scanf("%d%d%d",&n,&m,&k);
for(i=0;i<=m;i++) f[i] = 1.0;
int a;double p;
while(n--)
{scanf("%d%lf",&a,&p);//a代表当前发动机编号,p代表当前成功概率
p[a]=p[a]*(1.0-p);}//更新失败概率
for (i=1;i<=m;i++) s[i]=1.0-f[i];//计算成功概率
ss[0] = 1;
for(int i = 1; i <= m ;++i){//i从1到m递增
for(int j = i; j >= 1; --j)//j从i到1递减
ss[j] = ss[j]*f[i]+ss[j-1]*s[i];
ss[0] = ss[0]*f[i];
}
double es = 0;//es代表至少有K台成功的概率
for(int i = k; i <= m; ++i)
es += ss[i];//ss[i]代表在m台中有i台成功的概率
printf("%.3f\n",es);
return 0;
}
【归纳】dp[i][j]=P(i个独立事件中发生j个)=dp[i-1][j]*P(事件i不发生)+dp[i-1][j-1]* P(事件i发生)
【题目】
把N个标记在地上围成一圈,任意指定一个起点和绕行方向,给这些标记依次从0到N-1编号。机器人从任意位置出发,第一步走到标记0,之后每一步都随机选择向前或向后移动一个标记,当所有标记都被机器人走过后,行走结束。用(N,M)来记录一次行走,其中M代表行走结束的位置。
输入T次记录,输出T行,其中第i行输出前i次行走都发生的概率。输出答案对1000000007取模的值。
【思路】
特殊情况:结束位置为0当且仅当N==1时为必然事件,N>1时为不可能事件。
用计算机模拟试验,用频率逼近概率。
用p(i)代表事件i的概率,事件i:结束位置为i。其等效于通过若干步走到i-1(不经过i),再通过若干步绕一个圈走到i+1,再走到i。一共有n-1种终点,这些终点地位相同,所以p(i)=1/(n-1)(1≤i≤n-1)
各次行走之间彼此互相独立,因此前i次行走都发生的概率=前i-1次行走都发生的概率×第i次行走发生的概率。
优化:只要有一次行走不可能发生,接下来的结果都是0。
#include
#define LL long long
const int mod=1000000007;
LL fraction_mod(LL a,LL b){
LL ksm=1,x=b,y=mod-2;
for (;y;y>>=1,x=x*x%mod)
if (y&1)(ksm*=x)%=mod;
return a*ksm%mod;
}
int main(){
LL ans=1;
int t;scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
if(ans!=0){
if(m==0)
{if(n>1) ans=0;}
else ans=ans*fraction_mod(1,n-1)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
【题目】输入A,B,C三个点的坐标,P是三角形ABC中任意一点,计算m=max{ SPAB,SPBC,SPCA }的期望E,输出36*E的值。(保证它是整数)
【思路】
临界点:重心。设从A,B,C出发的中线的另一端点分别为A,B,C,三角形重心为O,通过推导,可画出max函数各分段对应的区域,是3个四边形,如图1。
连续概率:点P只可能落到这3个四边形中的一个(边界情况可忽略),所以可将总期望分解为3个区域中的期望之和。(1)以四边形ADGF为例,要计算出其内部所有的点P与BC连线构成的三角形面积的期望。约定A到BC距离为6h,BC=2l。以DF为界分成上下两部分,分别进行讨论。
(1)根据几何概型,随机点P落到两个区域△ADF与△GDF的概率之比等于它们的面积之比3:1;计算出的期望需要除以△ABC面积。所以有四边形的全期望公式
E 1 = [ 3 4 E ( S D F 上 ) + 3 4 E ( S D F 下 ) ] / S A B C E_1=[\frac{3}{4}E(S_{DF上})+\ \frac{3}{4}E(S_{DF下})]/S_{ABC} E1=[43E(SDF上)+ 43E(SDF下)]/SABC
(2) 用 h ′ ‾ 代表取得的 h ’的平均值, 用\overline{h\prime}代表取得的h’的平均值, 用h′代表取得的h’的平均值,因为三角形内部任意点到指定边距离的期望等于指定边上的高×1/3,所以对于DF上,h’=3h/3=h,E(SDF上)=½BC·(3h+h’)=4hl;对于DF下,h’=h/3,E(SDF下)=½BC·(3h-h’)=8hl/3。所以 E 1 = 11 3 h l / 6 h l = 11 18 S E_1=\frac{11}{3}hl/6hl=\frac{11}{18}S E1=311hl/6hl=1811S。(S为△ABC面积)(3)因为3个四边形面积相等,具有对称性,所以它们地位相等,△ABC的全期望公式
E = 1 3 × E 1 + 1 3 × E 1 + 1 3 × E 1 = E 1 E=\frac{1}{3}\times E_1+\frac{1}{3}\times E_1+\frac{1}{3}\times E_1=E_1 E=31×E1+31×E1+31×E1=E1
(4)答案为36×E1=22S,S可用三角形面积的坐标计算公式计算。
#include
#define LL long long
int main(){
LL x1,y1,x2,y2,x3,y3;
while(scanf("%lld%lld%lld%lld%lld%lld",&x1,&y1,&x2,&y2,&x3,&y3)!=EOF){
LL s=(x1-x2)*(y1-y3)-(x1-x3)*(y1-y2);
if(s<0) s=-s;
printf("%lld\n",11*s);
}
return 0;
}
【题目】2人进行一个取石子游戏,有数量为n的一堆石子,两人轮流从中取走1~m个,最先取光石子的一方获胜。
如果游戏的双方使用的都是最优策略,请输出先走还是后走的人能赢。
【分析】当石子数恰好剩下(m+1)时,上一次取的人必定获胜。进一步倒推,第一个使石子数达到(m+1)的整数倍的人必定获胜。所以关键在于初始值n:n% (m + 1) !=0,则先手胜;否则,后手胜。
#include
using namespace std;
int main(){
int c;cin>>c;
while(c--){
int n,m;//n为石子总数,m为一次最多可以取走的石子数
cin>>n>>m;
if(n%(m+1)!=0) cout<<"first\n";
else cout<<"second\n";
}
return 0;
}
【题目】
甲乙两个人在拍卖会上竞价,规则如下:刚开始底价为0,两个人轮流开始加价,每次加价的幅度要在1~N之间,当价格大于或等于成本价 M 时,这次叫价的人就赢得竞拍。假设甲乙两人每次都以对自己最有利的方式进行加价,由甲先开始加价,试求甲第一次要出多少价格才能赢得竞拍。
【思路】
本题是上一题的逆过程。
当出价与成本价恰好相差(n+1)时,上一次取的人必定获胜。进一步倒推,第一个使出价与成本价恰好相差(n+1)的整数倍的人必定获胜。所以关键在于初始值n:m%(n+1)!=0,则先加价者胜;否则,后加价者胜。
#include
using namespace std;
int main(){
int m,n;//m代表成本价,n代表加价上限
while(cin>>m>>n){
if(m<=n) //此时可能有多解
{cout<<m++;
for(;m<=n;m++) cout<<" "<<m;}
else if(m%(n+1)==0) cout<<"none";
else cout<<m%(n+1);
cout<<"\n";
}
return 0;
}
有2堆石子,每次需要从其中1堆取至少1颗,或者从两堆取相同的数量,最后取完的人获胜。
用数对(a,b)来记录这2堆石子的状态(a a [ k ] = I N T ( 1 + 5 2 k ) , b [ k ] = a [ k ] + k , a[k]=INT(\frac{1+\sqrt5}{2}k),b[k]= a[k] + k, a[k]=INT(21+5k),b[k]=a[k]+k,其中INT(X)表示取小数X的整数部分。
奇异局势的性质:
【题目】
有2堆石子(输入保证前者数量<=后者),2人按上述规则进行游戏,假设双方都采取最好的策略,问先手是胜者还是败者。如果胜利,输出后手在先手第一次取子后面临的奇异局势。(代表局势的两个分量前者<后者;如果有2种走法,先输出同取)
Sample Input
1 2
5 8
4 7
2 2
0 0
Sample Output
0
1
4 7
3 5
0
1
0 0
1 2