首先我们先考虑期望要买多少张,我们设计状态是F[i]表示已有i张,要集齐n张还要买几张。为什么我们不直接设计状态F[i]为:有i张邮票的期望购买次数呢?因为你如果设计成这个状态的话就无法转移,你可以将状态之间用一个有向图表示,其中边权为转移的概率,那么我们发现一个状态的出边的边权和为1,如果你设计的是第二种状态,我们会发现转移出去的概率和是不等于1的。那我们就得到转移方程为 F[i]=(i/n)*(F[i]+1)+((n-i)/i)*(F[i+1]+1)。
但是我们发现钱数并不好统计,因为买第k张的钱数是k,而我们设计的状态是收集了i种时还要再买几张,因此我们可以这样等价转化一下,我们认为1张邮票的代价是1元,如果买了这个邮票,那么后面的邮票价格全都加1,那我们设计状态G[i]表示已有i张,要集齐n张还要花多少钱,那么我们就有:G[i]=(i/n)(G[i]+1+F[i])+((n-i)/n)(G[i+1]+1+F[i+1]),可以这样理解:如果这次买的和之前的相同,那么后面买的F[i]张价格全都上升1,如果买的和之前的不同,那么后面买的F[i+1]张价格全都上升1,再加上这张的价格1,相当与是费用提前计算。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define maxn 10005 6 double f[maxn],g[maxn]; 7 int n; 8 9 int main() 10 { 11 scanf("%d",&n); 12 for (int i=n-1;i>=0;i--) 13 f[i]=f[i+1]+(double)n/(n-i); 14 for (int i=n-1;i>=0;i--) 15 g[i]=g[i+1]+f[i+1]+((double)(n*i)/((n-i)*n))*f[i]+((double)n/(n-i)); 16 printf("%.2f\n",g[0]); 17 return 0; 18 }