专栏文章
题解:P12857 [NERC 2020 Online] Hit the Hay
P12857题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioy29yo
- 此快照首次捕获于
- 2025/12/03 03:01 3 个月前
- 此快照最后确认于
- 2025/12/03 03:01 3 个月前
算上父母有五种状态:婴儿状态 、父母入睡婴儿状态 、父母未入睡婴儿状态 。我们称为状态 。
对于时间离散的情况,我们可以直接设 表示当前处于状态 ,距离夜晚结束还有 时刻的最优策略下睡眠的期望时间。
但是非常可惜的是,这里时间是连续的。
不过我们还是可以考虑一个最小时间单位 。
以 为例,写出转移:
等式两边同时减掉 :
再同时除掉 :
左边是导数定义,右边的 相当于是函数 在 处的导数 。带入化简得到:
同理我们还可以写出 的转移:
然后是状态 。现在已经是最佳状态,如果现在不决策入睡其他状态就更不会睡了,所以:
最麻烦的是状态 ,因为可以选择是否入睡。具体地:
先研究取到第一项的情况,即 。
那么上式即:
把第一项减去第二项得到 。如果取到第二项,则该式 ,即:
感性上, 应该是单调递增的。所以在解出来 的微分方程组之后,我们可以二分出满足上式最小的 ,然后在 大于这个值的时候解出 取第二项的微分方程组。
具体地,我们的算法流程应该为:
- 求解如下常系数一阶线性微分方程组:
-
二分找到满足 的最小的 为 ,则上述解的定义域为 。
-
求解如下常系数一阶线性微分方程组,其中 ,初值为 时与第一步中的解数值相同:
- 答案为 。
最后的问题是,如何求解一阶线性微分方程组。
第一种方法是求精确解。我们有:
【常系数一阶线性微分方程组解的结构】 对于常系数一阶线性微分方程组 ,其中对向量求导定义为对向量每一维求导,其通解形式为 ,其中向量函数定义为对每一维代入自变量,矩阵指数定义为泰勒级数, 为特解。
至于特解,官方题解说可以观察到 每一行的和都是 ,所以 是 的特征值,故特解可以被看成线性函数。
但是这种方法麻烦完了。既然这题概率不取模,我们完全可以用基于精度的方法。
考虑四阶 Runge–Kutta 法。设 ,下面简述对于微分方程 如何求,微分方程组改成向量就行。
我们取步长 ,设 。
使用拉格朗日中值定理,存在 ,使得 。
我们使用 区间上的平均斜率来近似 。
设 。把它们加权平均得到 ,直接估算 。
取步长 ,时间复杂度为 。我取的是 。
代码
CPP#include<bits/stdc++.h>
#define File(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define ll long long
#define F(i,a,b) for(int i(a),i##i##end(b);i<=i##i##end;++i)
#define R(i,a,b) for(int i(a),i##i##end(b);i>=i##i##end;--i)
using namespace std;
const int C=50000;
double k,p0,p1,p2,q0,h;
inline array<double,4> calc1(double x,const array<double,4>&y){
array<double,4>res;
res[0]=y[0]*p0-y[3]*p0;
res[1]=1+y[1]*p1-(q0*y[0]+(1-q0)*y[2])*p1;
res[2]=1+y[2]*p2-y[1]*p2;
res[3]=res[1];
return res;
}
inline array<double,4> calc2(double x,const array<double,4>&y){
array<double,4>res;
res[0]=y[0]*p0-y[3]*p0;
res[1]=1+y[1]*p1-(q0*y[0]+(1-q0)*y[2])*p1;
res[2]=1+y[2]*p2-y[1]*p2;
res[3]=y[3]*p1-(q0*y[3]+(1-q0)*y[2])*p1;
return res;
}
array<double,4> operator+(const array<double,4>&a,const double&b){return {a[0]+b,a[1]+b,a[2]+b,a[3]+b};}
array<double,4> operator+(const array<double,4>&a,const array<double,4>&b){return {a[0]+b[0],a[1]+b[1],a[2]+b[2],a[3]+b[3]};}
array<double,4> operator*(const double&b,const array<double,4>&a){return {a[0]*b,a[1]*b,a[2]*b,a[3]*b};}
double x[C*2];
array<double,4>y[C*2];
inline void solve1(){
y[0]={0,0,0,0};
array<double,4>k1,k2,k3,k4;
F(i,1,C){
k1=calc1(x[i-1],y[i-1]);
k2=calc1(x[i-1]+h/2,y[i-1]+h/2*k1);
k3=calc1(x[i-1]+h/2,y[i-1]+h/2*k2);
k4=calc1(x[i-1]+h,y[i-1]+h*k3);
y[i]=y[i-1]+h/6*(k1+2*k2+2*k3+k4);
}
}
inline void solve2(int l){
array<double,4>k1,k2,k3,k4;
F(i,l,C){
k1=calc2(x[i-1],y[i-1]);
k2=calc2(x[i-1]+h/2,y[i-1]+h/2*k1);
k3=calc2(x[i-1]+h/2,y[i-1]+h/2*k2);
k4=calc2(x[i-1]+h,y[i-1]+h*k3);
y[i]=y[i-1]+h/6*(k1+2*k2+2*k3+k4);
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
int T;
cout<<fixed<<setprecision(12);
for(cin>>T;T;--T){
cin>>k>>p0>>p1>>p2>>q0;
p0=log(p0),p1=log(p1),p2=log(p2);
h=k/C;
F(i,0,C) x[i]=i*h;
solve1();
int l=0,r=C+1;
while(l<r){
int mid=(l+r)>>1;
if(-q0*(y[mid][1]-y[mid][0])*p1>=1) r=mid;
else l=mid+1;
}
if(l!=C+1) solve2(l);
cout<<y[C][0]<<"\n";
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...