1 条题解

  • 0
    @ 2025-8-24 21:58:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 斯德哥尔摩
    **

    搬运于2025-08-24 21:58:24,当前版本为作者最后更新于2018-10-26 21:46:58,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P4274 [NOI2004]小H的小屋

    考试的题,然后模拟退火成功弄到了9090分。

    不过当然是对着测试数据一次一次滴调玄学参数弄成的。。。

    然后DPDP正解被集体hackhack了。。。

    主要看的是这个巨佬的题解加上自己的一发YYYY链接

    #1:DP+优化\#1:\text{DP+优化}

    据说是从O(n5)O(n^5)优化到了O(n4)O(n^4),然后还很松,进化成了O(n3)O(n^3)。。。

    反正蛮神奇的,表示没有看懂优化。。。

    有时间再填坑吧,这里主要介绍第二种做法。。。

    #2:贪心\#2:\quad\text{贪心}

    我们知道,对于一块长度一定的矩形,将其均分,一定能得到最小面积的矩形分割方案。

    证明?如下:

    假设当前这个矩形长度为4x4x,并且矩形对角线斜率为kk

    再假设我们当前要分成两个矩形,那么有两种分法:x,3xx,3x2x,2x2x,2x

    显然$$S_1=kx^2+9kx^2=10kx^2>S_2=4x^2+4x^2=8x^2$$

    然后分3,4,5,...3,4,5,...块时依次类推。

    于是得证。

    当然还可以用不等式证明,比这个更严谨,不过为了通俗易懂我就这么做了。

    然后就可以贪心了。

    n%m==0n \% m==0时,直接均分南北墙就是最优值。

    而当n%m>0n \% m>0时,应该使其余数尽量均分在每一段上。

    所以此时整个图形可以分为22段:

    前段有mn%mm-n \% m块北墙草坪,每段对应nm\lfloor\frac{n}{m}\rfloor块南墙草坪;

    后段有n%mn \% m块北墙草坪,每段对应nm+1\lfloor\frac{n}{m}\rfloor+1块南墙草坪。

    所以可以枚举两段长度,取最小面积。

    并且对于其中一段,其分配的面积也应该平均分配,如果余数不为00,也要把余数再平均分配。

    然后,我们发现:枚举的长度对应的面积是单峰的!

    所以当发现一个长度对应的面积大于当前最优解就可以直接跳出循环输出答案了。

    然后就做完了。

    时间复杂度是O(100)O(100),跑的飞起。。。

    记得到本蒟蒻的博客里逛逛哦!(那个DPDP的更新可能会贴在这里。)

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define MAXN 110
    using namespace std;
    int n,m,lnorth,rnorth,lsouth,rsouth;
    double k1,k2,ans=(1LL<<62);//记得开最大最
    inline double square(int x){return 1.00*x*x;}
    inline double min(double x,double y){return x<y?x:y;}
    inline double Area(int num,int len,double k){//求出面积
    	if(!num)return 0;//记得特判,防止除0!
    	double s=square(len/num)*k*(num-len%num)+square(len/num+1)*k*(len%num);
    	return s;
    }
    void work(){
    	if(n%m==0){//第一种情况,直接特判掉就好
    		ans=Area(lnorth,100,k1)+Area(lnorth*lsouth,100,k2);
    		printf("%.1lf\n",ans);
    		return;
    	}
    	for(int i=lnorth*lsouth;i<=100-rnorth*rsouth;i++){
    		double area=Area(lnorth,i,k1)+Area(rnorth,100-i,k1)+Area(lnorth*lsouth,i,k2)+Area(rnorth*rsouth,100-i,k2);
    		if(ans>area)ans=area;//更新答案
    		else break;//运用单调性
    	}
    	printf("%.1lf\n",ans);
    }
    void init(){
    	scanf("%lf%lf%d%d",&k1,&k2,&m,&n);
    	lnorth=m-n%m;rnorth=n%m;
    	lsouth=n/m;rsouth=n/m+1;//求出前段与后段的南北墙的草地块数
    }
    int main(){//主函数So easy!
    	init();
    	work();
    	return 0;
    }
    
    
    • 1

    信息

    ID
    3239
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者