【学术篇】烧水问题 打表找规律做法

md打表题.
按照贪心原则, 只要有烧开过的, 能热传递就热传递(反正没有热量损失)
所以我们只需要按照以下流程图(唉这个图要是崩了别埋怨我, 我也不想的嘛←_←)做即是最优解:

开始=>start: 开始
结束=>end: 结束
赋值=>operation: s=0,i=1
烧水=>operation: s=s+(4200*(100-T[i])/n)
遍历兑水=>operation: For j=i+1..n:T[i]=T[j]=(T[i]+T[j])/2
完成=>condition: i<=n
输出=>inputoutput: 输出s
开始->赋值->完成(yes,right)->烧水->遍历兑水(right)->完成
完成(no)->输出->结束

然后就得到了一个\(O(n^2)\)的做法.
显然无法通过此题.
但是我们可以打表得到一个数列... 前几项差不多长这样...

\[420000,315000,262500,229687.5,206718.75,189492.1875,175957.03125,164959.716796875 \]

然后找规律即可.

根据所学数列相关知识, 我们认为这些项之间的差/比应该会有一定的关系...
但是不管是差一下还是比一下都找不出什么显而易见的规律...
我们发现烧水的时候是有\(/n\)这种操作的..
我们就\(*n\)逆回去再看..
我们便可以设数列\(\{B_i\}\), 满足\(B_i=i*s_i\)..
然后再比就比出规律了..
发现\(\frac{B_{i+1}}{B_i}=\frac{2i+1}{2i}=1+\frac{1}{2i}=\frac{s_{i+1}*(i+1)}{s_i*i}\)..
我们就可以化简出\(O(n)\)的递推式\(s_{i+1}=\frac{(1+\frac{1}{2i})*s_i*i}{i+1}\)
然后这样已经足以通过此题了..

#include <cstdio>
int main(){
    int n;scanf("%d",&n);
    double s=420000;
    for(int i=1;i<n;++i)
        s=((1+1.0/(i+i))*s*i)/(i+1);
    printf("%.2lf",s);
}

但是式子长得不是很好看..太啰嗦..
我们可以收拾一下?
\(s_{i+1}=s_i*\frac{i+0.5}{i+1}\)
然后加上一点点奇怪的压行技巧..就会出现warning...
就可以做成这样

#include<cstdio>
main(){double n,s=4.2e5,i=1;scanf("%lf",&n);for(;i<n;)s*=(i+0.5)/++i;printf("%.2lf",s);}

我应该是做不到更短了...

打表题做起来应该很愉快才对~OvO

posted @ 2018-02-04 08:52  Enzymii  阅读(186)  评论(0编辑  收藏  举报