2018.09.27 bzoj3029: 守卫者的挑战(概率dp)

传送门
概率dp经典题目。
直接 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示当前是第i次挑战,已经胜利了j次,目前的背包剩余空间是k。
然后用前面的转移后面的就行了。
注意第三维可能是负数,需要用一些技巧转化一下(比如把整个数组的下标向右平移)
代码:

#include
#define N 205
using namespace std;
int n,l,K,a[N],tmp;
double p[N],f[2][205][605],ans;
inline int calc(int x){
	if(x<0)return x=max(-n,x),N+x;
	return x=min(n,x),N+x;
}
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
int main(){
	n=read(),l=read(),K=read();
	for(int i=1;i<=n;++i)p[i]=read()/100.0;
	for(int i=1;i<=n;++i)a[i]=read();
	f[0][0][calc(K)]=1;
	for(int i=0;i<n;++i){
		memset(f[tmp^1],0,sizeof(f[tmp^1]));
		for(int j=0;j<=n;++j)
			for(int k=-n;k<=n;++k){
				f[tmp^1][j+1][calc(k+a[i+1])]+=f[tmp][j][calc(k)]*p[i+1];
				f[tmp^1][j][calc(k)]+=f[tmp][j][calc(k)]*(1-p[i+1]);
			}
		tmp^=1;
	}
	for(int i=l;i<=n;++i)for(int j=0;j<=n;++j)ans+=f[tmp][i][calc(j)];
	printf("%.6lf",ans);
	return 0;
}

你可能感兴趣的:(#,dp,#,概率dp)