【数学,DP】AcWing 232. 守卫者的挑战

这题看起来不难然而一堆细节。。

分析

首先不难看出这是一个类似于背包的 dp 问题。

考虑状态的设计:\(f(i, j, k)\) 表示当前考虑到第 \(i\) 个挑战,当前背包剩余容量\(j\),前 \(i\) 个挑战中已经成功了 \(k\) 个。

那么我们可以进一步写出转移方程:

  • \(i\) 个挑战失败时:\(f(i-1, j, k) \to f(i, j, k)\)
  • \(i\) 个挑战成功时:\(f(i-1, j, k) \to f(i, j+w_i, k+1)\)

接下来就是细节问题了:

约定 \(V\) 为地图残片的数量。

  • 首先,注意到 \(\sum w_i\) 会非常大,直接开数组自然不行,事实上当背包容量 \(\geq V\) 的时候能够保证装下所有的地图残片,因此我们可以对状态表示进行调整:对于 \(f(i, V, k)\) 表示当前背包剩余容量 \(\geq V\)\(i, k\) 定义不变。
  • 其次,因为我们是对挑战直接进行 \(1\to n\) 的扫描,有可能在过程中出现背包容量为负的情况,但是我们不能直接舍弃这类状态,因为有可能在未来这样的状态可以转移到背包容量为正的情况,那么我们需要想办法将这类状态也保存,具体做法是:将 \(j\) 整体加上一个 \(V\) 的偏移量(类似于将数轴平移)。
// Problem: 守卫者的挑战
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/234/
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=220;

int n, L, K, V;
double p[N];
int w[N];

int main(){
	cin>>n>>L>>K;
	rep(i,1,n) cin>>p[i], p[i]/=100;
	rep(i,1,n){
		read(w[i]);
		if(w[i]==-1) V++;
	}
	K=min(V, K);
	
	double f[n+1][V<<1|1][n+1];
	memset(f, 0, sizeof f);
	f[0][V+K][0]=1;
	
	rep(i,1,n){
		rep(j,0,V<<1) rep(k,0,n){
			f[i][j][k]+=(1.0-p[i])*f[i-1][j][k];
			if(j+w[i]>=0 && k<n) f[i][min(j+w[i], V<<1)][k+1]+=p[i]*f[i-1][j][k];
		}
	}
	
	double res=0;
	rep(i,V,V<<1) rep(j,L,n) res+=f[n][i][j]; 
	printf("%.6lf\n", res);
	
	return 0;
}
posted @ 2022-02-15 12:05  HinanawiTenshi  阅读(48)  评论(2编辑  收藏  举报