这个题有两种不同的思路:
1、先将m-=n,将题目转换为所有关都通过且多收集了m个
按y值从小到大排序,然后倒着dp,设f[i][j]表示还剩下i个关没通过,还有j个星星没有收集
然后枚举转移即可
2、按y值从小到大排序,贪心地考虑把前面的都闯过,后面不断过y值大的关来刷星星
然后记录收集的星星的期望和期望步数,枚举的时候还要考虑后面的是否一定得刷两颗星
我写了第一种的:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 2005
#define M 4005
using namespace std;
int n,m;
double ans,f[M][M];
const double inf=1e16;
struct qwq{
double x,y;
bool operator <(const qwq &b) const {
return y<b.y;
}
}a[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y),a[i].x/=1000.0,a[i].y/=1000.0;
sort(a,a+n); m-=n;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j]=inf;
f[n][m]=0;
for(int i=n-1;i>=0;i--){
double tmp=a[i].x+a[i].y;
for(int j=0;j<m;j++){
f[i][j]=min(f[i][j],1.0/tmp+f[i+1][j]*a[i].x/tmp+f[i+1][j+1]*a[i].y/tmp);
f[i][j]=min(f[i][j],f[i+1][j+1]+1.0/a[i].y);
}
f[i][m]=min(f[i][m],f[i+1][m]+1.0/tmp);
}
printf("%.7lf",f[0][0]);
return 0;
}
第二种参考zyz学长的qwq:
#include<bits/stdc++.h>
#define double long double
using namespace std;
const int N = 4005;
int n,m;
double x[N],y[N];
int p[N];
bool cmp(int xx,int xy)
{
return y[xx]<y[xy];
}
double b[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%Lf%Lf",&x[i],&y[i]);
x[i]/=1000;y[i]/=1000;
}
for(int i=1;i<=n;i++)p[i]=i;
sort(p+1,p+n+1,cmp);
b[0]=1;
double ans=0;
double now=1;
for(int i=1;i<=n;i++)
{
int t=p[i];
if(m-(n-i+1)*2>=i-1)
{
double tmp=0;
for(int j=i;j<=n;j++)
{
tmp+=1/y[p[j]];
}
ans+=b[m-(n-i+1)*2]*tmp;
now-=b[m-(n-i+1)*2];
b[m-(n-i+1)*2]=0;
}
ans+=now/(x[t]+y[t]);
for(int j=2*n;j>=0;j--)
{
b[j]=0;
if(j>=1)b[j]=b[j-1]*x[t]/(x[t]+y[t]);
if(j>=2)b[j]+=b[j-2]*y[t]/(x[t]+y[t]);
}
}
printf("%.10Lf\n",ans);
return 0;
}