题目描述
把总质量为1kg的水分装在n个杯子里,每杯水的质量均为(1/n)kg,初始温度均为0℃。现需要把每一杯水都烧开。
我们可以对任意一杯水进行加热。把一杯水的温度升高t℃所需的能量为(4200*t/n)J,其中,“J”是能量单位“焦耳”。
如果一旦某杯水的温度达到100℃,那么这杯水的温度就不能再继续升高,此时我们认为这杯水已经被烧开。显然地,
如果直接把水一杯一杯地烧开,所需的总能量为(4200*100)J。
在烧水的过程中,我们随时可以在两杯温度不同的水之间进行热传递操作。热量只能从温度较高的那杯水传递到
温度较低的那杯水。由于两杯水的质量相同,所以进行热传递操作之后,原来温度较高的那杯水所降低的温度总是等于
原来温度较低的那杯水所升高的温度。
一旦两杯水的温度相同,热传递立刻停止。(必须相同才停止,不能中途停 或是把一个的全传给另一个)
为了把问题简化,我们假设:
1、没有进行加热或热传递操作时,水的温度不会变化。
2、加热时所花费的能量全部被水吸收,杯子不吸收能量。
3、热传递总是隔着杯子进行,n杯水永远不会互相混合。
4、热传递符合能量守恒,而且没有任何的热量损耗。
在这个问题里,只要求把每杯水都至少烧开一遍就可以了,而不要求最终每杯水的温度都是100℃。我们可以用如下操作把两杯水烧开:先把一杯水加热到100℃,花费能量(4200*100/2)J,然后两杯水进行热传递,直到它们的温度都变成50℃为止,最后把原来没有加热到100℃的那杯水加热到100℃,花费能量(4200*50/2)J,此时两杯水都被烧开过了,当前温度一杯100℃,一杯50℃,花费的总能量为(4200*75)J,比直接烧开所需的(4200*100)J少花费了25%的能量。
你的任务是设计一个最佳的操作方案使得n杯水都至少被烧开一遍所需的总能量最少。
输入输出格式
输入格式:
输入文件只有一个数n。
输出格式:
输出n杯水都至少被烧开一遍所需的最少的总能量,单位为J,四舍五入到小数点后两位。
输入输出样例
输入样例#1:
2
输出样例#1:
315000.00
说明
1≤n≤50000
虽然标签是数学,但它仍然是个贪心问题
既然要使烧水的总能量最少,那就**少烧水**,所以我们可以用烧开了的水传递热量,
把它的热量**尽可能的都传给**剩下的几杯,而且一杯水烧开了一次之后,就不用管最终温度了,
我们就算把它的温度传递至只剩0(虽然不可能,∵每次/2)也不会对最终结果产生任何影响
我们把第一杯烧开了之后往后传它的热量,再烧开下一杯,以此类推,相邻两杯的热量是
有一定的关系的,然后,可以推相邻两个的所需热量公式(也就是它们的倍数关系),
看这个关系是几倍即可
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
double ans1,ans;
int main()
{
//freopen("water.in","r",stdin);
//freopen("water.out","w",stdout);
scanf("%d",&n);
double t=100.0,m=4200.0;
ans1=m*t/n;//第一杯水,需要升高100℃
ans+=ans1;
for(int i=2;i<=n;i++) //2~n杯水
{
ans+=ans1*(2*(i-1)-1)/(2*(i-1));
ans1=ans1*(2*(i-1)-1)/(2*(i-1)); //随时更新“上一杯水”所需要的热量
//第i杯水烧开所需能量 是 第i-1杯水烧开所需能量*(2*(i-1)-1)/(2*(i-1))
//ans1*=(2*(i-1)-1)/(2*(i-1));不能这样写,因为“/”是下取整,
//如果得到的是分数的话,很可能取到,毕竟不能像"3/4"一样存,3/4得到的是 商:0
//如果取到0,那ans1就变为0了,但是,如果把ans1乘过来写(ans1* (2*(i-1)-1)/(2*(i-1)))
//根据从左往右的运算原则 (2*(i-1)-1)/(2*(i-1))<1,所以一定不可能商为0
}
printf("%.2lf",ans);
return 0;
}
/* 32 83451.27 */