VJ传送门
首先因为这个能记住前 k k k条路很不好搞
因为 d p dp dp过程中无法知道它记住的是那些门
所以就需要把门抽象成两类
一种是进去后回到起点,花费总时间 v a l 1 val_1 val1,有 s 1 s_1 s1扇门
一种是进去后就能逃生,花费总时间 v a l 2 val_2 val2,有 s 2 s_2 s2扇门
这样把所有第一类门权值看作 v a l 1 s 1 \frac{val_1}{s_1} s1val1,把所有第二类门看作 v a l 2 s 2 \frac{val_2}{s_2} s2val2
因为对于同一类门来说,被选到的概率均等,造成的影响相同,所以可以用期望代替
这样我们就可以不去关心目前记住的是那些门,只需要知道记住了多少门就行!!
设 f [ i ] f[i] f[i]为记住了 i i i扇门,逃生还需花费的时间
先对 k k k处理一下, k = m i n ( k , s 1 ) k=min(k,s_1) k=min(k,s1)
那么终止点就是 f [ k ] = ( f [ k ] + v a l 1 ) ∗ p 1 + v a l 2 ∗ p 2 f[k]=(f[k]+val_1)*p_1+val_2*p_2 f[k]=(f[k]+val1)∗p1+val2∗p2
其中 p 1 = s 1 − k n − k p_1=\frac{s_1-k}{n-k} p1=n−ks1−k表示此时去第一类门
其中 p 2 = s 2 n − k p_2=\frac{s_2}{n-k} p2=n−ks2,表示去第二类门
然后一般情况递推式是 f [ i ] = ( f [ i + 1 ] + v a l 1 ) ∗ s 1 − i n − i + v a l 2 ∗ s 2 n − i f[i]=(f[i+1]+val_1)*\frac{s_1-i}{n-i}+val_2*\frac{s_2}{n-i} f[i]=(f[i+1]+val1)∗n−is1−i+val2∗n−is2
f [ 0 ] f[0] f[0]就是答案
#include <bits/stdc++.h>
using namespace std;
int n,k;
double f[109];
int main()
{
int t,casenum=0; cin >> t;
while( t-- )
{
cin >> n >> k;
double val1=0,val2=0,s1=0,s2=0;
for(int i=1;i<=n;i++)
{
double x; cin >> x;
if( x<0 ) val1 -= x,s1++;
else val2 += x,s2++;
}
if( s2==0 )
{
printf("Case %d: -1\n",++casenum);
continue;
}
if( s1>0.0 )val1 /= s1;
if( s2>0.0 )val2 /= s2;
k = min( k,(int)s1 );
double p1 = (s1-k)/(n-k), p2 = s2/(n-k);
f[k] = val1*p1+val2*p2;
f[k] /= (1.0-p1);
for(int i=k-1;i>=0;i--)
{
p1 = (s1-i)/(n-i), p2 = s2/(n-i);
f[i] = ( f[i+1]+val1 )*p1+val2*p2;
}
printf("Case %d: %.7lf\n",++casenum,f[0] );
}
}