题解 P4274 【[NOI2004]小H的小屋】
P4274 [NOI2004]小H的小屋
考试的题,然后模拟退火成功弄到了
不过当然是对着测试数据一次一次滴调玄学参数弄成的。。。
然后
主要看的是这个巨佬的题解加上自己的一发
\#1:\text{DP+优化} :
据说是从
反正蛮神奇的,表示没有看懂优化。。。
有时间再填坑吧,这里主要介绍第二种做法。。。
\#2:\quad\text{贪心} :
我们知道,对于一块长度一定的矩形,将其均分,一定能得到最小面积的矩形分割方案。
证明?如下:
假设当前这个矩形长度为
再假设我们当前要分成两个矩形,那么有两种分法:
显然
然后分
于是得证。
当然还可以用不等式证明,比这个更严谨,不过为了通俗易懂我就这么做了。
然后就可以贪心了。
当
而当
所以此时整个图形可以分为
前段有
后段有
所以可以枚举两段长度,取最小面积。
并且对于其中一段,其分配的面积也应该平均分配,如果余数不为
然后,我们发现:枚举的长度对应的面积是单峰的!
所以当发现一个长度对应的面积大于当前最优解就可以直接跳出循环输出答案了。
然后就做完了。
时间复杂度是
记得到本蒟蒻的博客里逛逛哦!(那个
附代码:
#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;
}