Endless spin
给你一段长度为[1..n]的白色区间,每次随机的取一个子区间将这个区间涂黑,问整个区间被涂黑时需要的期望次数。n<=50
这篇博客港的很好了,相信我没有什么好港的。注意这里的dp是第二种用法的dp。
#include <cstdio>
using namespace std;
int T, n;
const int maxn=55;
long long f[maxn][2][maxn*maxn];
struct BigDouble{
long long ip; //整数部分
int a[maxn];
BigDouble(){}
BigDouble(long long x, long long y){
ip=x/y; x=x%y;
for (int i=0; i<maxn; ++i) x*=10, a[i]=x/y, x%=y; }
}E[maxn];
BigDouble operator+(const BigDouble &x, const BigDouble &y){
BigDouble re; re.ip=x.ip+y.ip;
for (int i=0; i<maxn; ++i) re.a[i]=x.a[i]+y.a[i];
for (int i=maxn-1; i>0; --i)
if (re.a[i]>9) ++re.a[i-1], re.a[i]-=10;
if (re.a[0]>9) ++re.ip, re.a[0]-=10;
return re;
}
BigDouble operator-(const BigDouble &x, const BigDouble &y){
BigDouble re; re.ip=x.ip-y.ip;
for (int i=0; i<maxn; ++i) re.a[i]=x.a[i]-y.a[i];
for (int i=maxn-1; i>0; --i)
if (re.a[i]<0) --re.a[i-1], re.a[i]+=10;
if (re.a[0]<0) --re.ip, re.a[0]+=10;
return re;
}
void print(BigDouble x){
BigDouble y; if (x.a[15]>4) y.a[14]=1;
y=y+x;
printf("%lld.", y.ip);
for (int i=0; i<15; ++i) printf("%d", y.a[i]);
puts(""); return; }
int main(){
scanf("%d", &T);
f[0][0][0]=1;
for (int i=0; i<=50; ++i) //当前考虑前i个数 ,第i个数是在集合内的
for (int j=0; j<=(i+1)*i/2; ++j){ // 区间个数为j
for (int k=i+1; k<=50; ++k) //设第k个数 也在集合内
f[k][1][j+(k-i)*(k-i-1)/2]+=f[i][0][j];
for (int k=i+1; k<=50; ++k)
f[k][0][j+(k-i)*(k-i-1)/2]+=f[i][1][j];
}
int A, all;
for (int i=1; i<=50; ++i)
for (int j=0; j<=i; ++j){ //第j个数在子集内
for (int k=0; k<=(j+1)*j/2; ++k){ //k个区间
A=(k+(i-j+1)*(i-j)/2); all=(i*(i+1)/2); //p=A/all;
if (all==A) continue;
E[i]=E[i]-BigDouble(f[j][0][k]*all, all-A);
E[i]=E[i]+BigDouble(f[j][1][k]*all, all-A);
}
}
while (T--){ scanf("%d", &n); print(E[n]); }
return 0;
}