洛谷 P1984 [SDOI2008] 烧水问题

探讨如何通过合理安排加热和热传递过程,使n杯水烧开所需的总能量最少。采用贪心策略,利用已烧开水的余温来减少整体加热成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

把总质量为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 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值