NOI2004——小H的小屋(动态规划)

这篇博客介绍了如何使用动态规划解决NOI2004中关于小H的小屋问题。通过预处理距离与草坪数量的关系,计算出不同状态下最小面积的决策过程。虽然没有采用决策单调性优化,但朴素的方法也能通过测试。

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

1505: [NOI2004]小H的小屋

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 102  Solved: 79
[Submit][Status][Discuss]
Description

小H发誓要做21世纪最伟大的数学家。他认为,做数学家与做歌星一样,第一步要作好包装,不然本事再大也推不出去。为此他决定先在自己的住所上下功夫,让人一看就知道里面住着一个“未来的大数学家”。 为了描述方便,我们以向东为x轴正方向,向北为y轴正方向,建立平面直角坐标系。小H的小屋东西长为100Hil(Hil是小H自己使用的长度单位,至于怎样折合成“m”,谁也不知道)。东墙和西墙均平行于y轴,北墙和南墙分别是斜率为k1和k2的直线,k1和k2为正实数。北墙和南墙的墙角处有很多块草坪,每块草坪都是一个矩形,矩形的每条边都平行于坐标轴。相邻两块草坪的接触点恰好在墙上,接触点的横坐标被称为它所在墙的“分点”,这些分点必须是1到99的整数。 小H认为,对称与不对称性的结合才能充分体现“数学美”。因此,在北墙角要有m块草坪,在南墙角要有n块草坪,并约定m≤n。如果记北墙和南墙的分点集合分别为X1,X2,则应满足X1 X2,即北墙的任何一个分点一定是南墙的分点。 由于小H目前还没有丰厚的收入,他必须把草坪的造价降到最低,即草坪的占地总面积最小。你能编程帮他解决这个难题吗?
Input

仅一行,包含4个数k1,k2,m,n。k1和k2为正实数,分别表示北墙和南墙的斜率,精确到小数点后第一位。m和n为正整数,分别表示北墙角和南墙角的草坪的块数。
Output

一个实数,表示草坪的最小占地总面积。精确到小数点后第一位。 2≤m≤n≤100 南北墙距离很远,不会出现南墙草坪和北墙草坪重叠的情况
Sample Input
0.5 0.2 2 4

Sample Output
3000.0

解析:

         dp[i][j][k]表示长度为k,北边有i个草坪,南边有j个草坪时的最小面积

         由于北边的草坪数要少,即最后我们按北边来递推,可以不用考虑状态是否合法。。。

         所以我们先预处理出g[i][j]表示距离为i时,南边有j块草坪的最小面积。。。

         所以,dp[i][j][k]=min(dp[i][j][k],dp[i-1][j'][k']+kb*(k-k')^2+g[k-k'][j-j'])。。。

         据说,这道题要考虑决策单调性来优化。。。吾等弱菜只能朴素了,但还是能过

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define N 110
#define inf 0x3f3f3f3f

double kb,kn,g[N][N],dp[N][N][N];
int m,n;

int main()
{
    freopen("H.in","r",stdin);
    freopen("H.out","w",stdout);
    scanf("%lf%lf%d%d",&kb,&kn,&m,&n);
    for (int i = 0; i < N; ++i)
    for (int j = 0; j < N; ++j)
       g[i][j] = inf;
    g[0][0] =0;  //g[i][j]表示把南边划分成j个矩形,长度为i的最小面积
    for(int i=1;i<=100;i++)
        for(int j=1;j<=i;j++)
        {
            for(int k=j-1;k<i;k++)
            g[i][j]=min(g[i][j],g[k][j-1]+(i-k)*(i-k)*kn);
        }
    for (int i = 0; i < N; ++i)
    for (int j = 0; j < N; ++j)
    for (int k = 0; k < N; ++k)
        dp[i][j][k] = inf;
    dp[0][0][0]=0;
    for(int k=1;k<=100;k++)
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
            {
                for(int kx=i-1;kx<k;kx++)
                    for(int j1=i-1;j1<j;j1++)
                dp[i][j][k]=min(dp[i][j][k],dp[i-1][j1][kx]+(k-kx)*(k-kx)*kb+g[k-kx][j-j1]);
            }
    printf("%.1lf",dp[m][n][100]);
    return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值