牛客小白月赛13 小A买彩票(DP+分数约分)
链接:https://ac.nowcoder.com/acm/contest/549/C
来源:牛客网
题目描述
小A最近开始沉迷买彩票,并且希望能够通过买彩票发家致富。已知购买一张彩票需要3元,而彩票中奖的金额分别为1,2,3,4元,并且比较独特的是这个彩票中奖的各种金额都是等可能的。现在小A连续购买了n张彩票,他希望你能够告诉他至少能够不亏本的概率是多少。
输入描述:
一行一个整数N,为小A购买的彩票数量一行一个整数N,为小A购买的彩票数量一行一个整数N,为小A购买的彩票数量
输出描述:
输出一个最简分数a/b,表示小A不亏本的概率。若概率为1,则输出1/1,概率为0,则输出0/1。输出一个最简分数a/b,表示小A不亏本的概率。 \\若概率为1,则输出1/1,概率为0,则输出0/1。输出一个最简分数a/b,表示小A不亏本的概率。若概率为1,则输出1/1,概率为0,则输出0/1。
示例1
输入
2
输出
3/
4的30次方的规模显然没法暴力,根据题目描述考虑DP做法。设DP[i][j]表示买了第i张彩票得到j元钱的概率,根据题目描述可知只能由上一阶段的四种情况转移而来:dp[i][j]=1/4*dp[i-1][j-1]+1/4*dp[i-1][j-2]+1/4*dp[i-1][j-3]+1/4*dp[i-1][j-4];注意概率都相等。这样只需要统计sigma(dp[n][k])(k>=3*n)的概率,加起来就好了。然后就是另一个关键的地方:输出要求是分数形式。由于每次乘的数都是0.25,那么不妨转化为每次乘1再除以4.为了不爆long long,需要在每一轮乘完后进行约分(利用gcd),便能AC。
注意一个坑点:n=0必然不会亏,所以输出0/1.
#include <bits/stdc++.h> #define N 33 using namespace std; unsigned long long dp[N][4*N]; int n; unsigned long long gcd(unsigned long long a,unsigned long long b){ return b?gcd(b,a%b):a;} int main() { cin>>n; int i,j; unsigned long long mul=4;//mul实际上是最终的分母 for(i=1;i<=n;i++) { for(j=1;j<=4*n;j++)dp[i][j]=0; } dp[1][1]=dp[1][2]=dp[1][3]=dp[1][4]=1; for(i=2;i<=n;i++) { mul*=4; unsigned long long GCD=mul; for(j=4*n;j>=i;j--)//到第i轮最少也能获得i元 { if(j-1>=1)dp[i][j]+=dp[i-1][j-1]*1; if(j-2>=1)dp[i][j]+=dp[i-1][j-2]*1; if(j-3>=1)dp[i][j]+=dp[i-1][j-3]*1; if(j-4>=1)dp[i][j]+=dp[i-1][j-4]*1; GCD=gcd(GCD,dp[i][j]);//相当于求出dp[i][1~4*n]所有数和mul的最大公因数,再约分,只能这样一轮一轮整体约 } mul/=GCD; for(j=4*n;j>=i;j--)//约分 { dp[i][j]/=GCD; } } unsigned long long ans=0; for(i=3*n;i<=4*n;i++)//统计答案 { ans+=dp[n][i]; } if(n==0||ans==0) { cout<<"1/1";//如果不买彩票当然不会亏了 千万注意 return 0; } unsigned long long temp; while((temp=gcd(ans,mul))>1) { ans/=temp,mul/=temp; } cout<<ans<<'/'<<mul; }