【校內模擬】assignment(DP)

題面見校內OJ4693


題解:

考慮預處理f[k][i][j]f[k][i][j]表示最長的一段不超過kk的時候,將長度爲ii的序列分爲jj段的方案數。

kk相同的狀態之間轉移,顯然有f[i][j]=f[i1][j]+f[i1][j1]f[ik1][j1]f[i][j]=f[i-1][j]+f[i-1][j-1]-f[i-k-1][j-1],考慮新的這個數是否放在原來的最後一個段中就行了。

然後分成jj段,顯然就需要從nn種不同的數中選擇jj種就行了。

用long double可以避免炸精度。

然而我考場上覺得可能會炸就沒寫。

痛失難得的一次AK機會

TM又是隻A了T2T3就差T1的一場考試


代碼:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

typedef long double ld;

cs int N=251;
ld f[N][N][N];
ld sum[N],C[N];
inline void init(){
	for(int re k=1;k<N;++k){
		f[k][0][0]=1;
		for(int re i=1;i<N;++i)
		for(int re j=1;j<=i;++j){
			f[k][i][j]=f[k][i-1][j]+f[k][i-1][j-1];
			if(i>=k+1)f[k][i][j]-=f[k][i-k-1][j-1];
		}
	}
}

inline void solve(){
	int n,m;scanf("%d%d",&m,&n);
	C[0]=1;for(int re i=1;i<=m;++i)C[i]=C[i-1]*(n-i+1)/i;
	for(int re k=1;k<=m;++k){
		sum[k]=0;
		for(int re j=1;j<=m;++j)
		sum[k]+=f[k][m][j]*C[j];
	}ld ans=0,tot=sum[m];
	for(int re i=1;i<=m;++i){
		ans+=(sum[i]-sum[i-1])*i/tot;
	}
	printf("%.7lf\n",(double)ans);
}

signed main(){
#ifdef zxyoi
	freopen("assignment.in","r",stdin);
#endif
	int T;scanf("%d",&T);init();
	while(T--)solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章