bzoj 1505 //1505:[NOI2004]小H的小屋

bzoj 1505 //1505:[NOI2004]小H的小屋   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1505

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

以下图片大部分摘自:

//算法合集之《从《小H的小屋》的解法谈算法的优化》可看下文
//https://wenku.baidu.com/view/8f2b07d276a20029bd642daf.html?rec_flag=default&sxts=1570067691943

算法一:朴素DP+边界优化

2.52s / 9.02MB / 877B C++

//1505:[NOI2004]小H的小屋
//在线测评地址https://www.luogu.org/problem/P4274
//题目看懂,样例也弄明白.
//算法合集之《从《小H的小屋》的解法谈算法的优化》可看下文
//https://wenku.baidu.com/view/8f2b07d276a20029bd642daf.html?rec_flag=default&sxts=1570067691943
//此文https://blog.csdn.net/jianing1996/article/details/8947770思路不错,摘抄如下
/*
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'])。。。

         据说,这道题要考虑决策单调性来优化。。。吾等弱菜只能朴素了,但还是能过
*/
//此文https://blog.csdn.net/Whjpji/article/details/7652813思路不错,摘抄如下
/*
朴素的动态规划时间复杂度为O(L^2 · m^2 · n),但是此题恰好可以卡过。

首先预处理g[k][i]即把东西长度为k的区域分给南墙,分成i块的最小面积。
可以轻易得出g[k][i] = g[k`][i - 1] + (k - k`)^2 * K2 (k` < k)。

然后在计算f[k][i][j]即把东西长度为k的区域分给南北两边的墙,北墙分成i块,南墙分成j块的最小面积。
则有:f[k][i][j] = f[k`][i - 1][j`] + g[k - k`][j - j`] (k` < k, j` < j)
最后的答案就是f[100][m][n]。
*/
//样例通过,提交AC.2019-10-3 20:45

//此文http://blog.sina.com.cn/s/blog_86942b1401016m3g.html思路不错,摘抄如下:

/*

算法思路:

决策性很明显,每一块草坪的建立都是在上一块的基础上进行的,所以不难想到是动态规划。方程是f[i,j,k]表示前i个单位长度,北墙有j块草坪,南墙有k块草坪时的最小面积。然后递推思想是枚举北墙的最后一块草坪长度len与对应几块南墙草坪p,所以递推方程是f[i,j,k] = min (f[i-len,j-1,k-p] + area[len,p])

然后就是area[len,p]的计算了,我开始就是以为全部均分就可以了。。忘记了草坪分点必须是整数。。。所以就只有20分了。area[len,p]也需要用dp来计算,方程为area[len,p] = min (area[len',p-1] + k2*sqr(len-len')) + k1*sqr(len)

到这里方程只能得70分,但是这个不是朴素方程的极限,还可以常数优化!首先是边界优化,注意一些细节,比如j<=k<=i,还有len<=ip<=k,但是这样还是超时!于是将所有的min函数改成if语句!然后就神奇的0.6s无压力过了。。。。。。

*/

#include <stdio.h>
#define maxn 105
#define INF 999999999
double k1,k2,g[maxn][maxn],f[maxn][maxn][maxn];
int m,n;
double min(double a,double b){
    return a<b?a:b;
}
int main(){
    int i,j,k,kx,jx;
    scanf("%lf%lf%d%d",&k1,&k2,&m,&n);
    for(i=0;i<maxn;i++)
        for(j=0;j<maxn;j++)
            g[i][j]=INF;
    g[0][0]=0;
    for(i=1;i<=100;i++)
        for(j=1;j<=i;j++)//此处写成for(j=1;j<=n;j++)可进一步优化,最小长度为1,j最大为i
            for(k=j-1;k<i;k++)
                g[i][j]=min(g[i][j],g[k][j-1]+(i-k)*(i-k)*k2);
    for(k=0;k<=100;k++)
        for(i=0;i<=m;i++)
            for(j=0;j<=n;j++)
                f[k][i][j]=INF;
    f[0][0][0]=0;
    for(k=1;k<=100;k++)
        for(i=1;i<=m;i++)
            for(j=1;j<=n;j++)
                for(kx=i-1;kx<k;kx++)//因一块草坪至少占一个刻度,故i-1块草坪,之后的刻度要从i-1取起.
                    for(jx=i-1;jx<j;jx++)//因北面的尺度>=南面的尺度,故北面有i-1块草坪,南面草坪>=i-1,故jx从i-1取起
                        f[k][i][j]=min(f[k][i][j],f[kx][i-1][jx]+(k-kx)*(k-kx)*k1+g[k-kx][j-jx]);
    printf("%.1lf\n",f[100][m][n]);
    return 0;
}

算法二:DP+决策单调性优化

1.36s / 18.13MB / 1.08KB C++

//此文http://blog.sina.com.cn/s/blog_86942b1401016m3g.html思路不错,摘抄如下:

/*

方程的单调性优化:

从决策点lenp出发。

从面积公式可以知道,对于一段长度的草坪,越均分面积约小。注意到len是从小到大枚举的,当len+1时,由于均分的思想,p对于上一次的最优决策只增不减,同理k增大时, p对于上一次的最优决策也是只增不减,所以可以记录上一次的最优决策p作为下界枚举。

时间复杂度:

朴素方程总时间复杂度为O(n^5),加一点边界优化接近O(n^4),再加上单调性优化低于O(n^4)。

*/

//此文https://blog.csdn.net/qpswwww/article/details/45391227思路不错,摘抄如下:

/*

 

 

*/

//样例通过,提交AC.2019-10-4 10:24奇怪,效率怎么没改进 2.60s / 18.14MB / 1023B C++
/*
for(kx=lastk[k][i-1][j];kx<k;kx++)//此处错写成for(kx=lastk[k][i][j];kx<k;kx++)
                    for(jx=lastj[k][i-1][j];jx<j;jx++)//此处错写成for(jx=lastj[k][i][j];jx<j;jx++)
*/
//样例通过,提交AC.2019-10-4 10:54,效率有提升 1.36s / 18.13MB / 1.08KB C++
#include <stdio.h>
#include <string.h>
#define maxn 105
#define INF 999999999
double k1,k2,g[maxn][maxn],f[maxn][maxn][maxn];
int m,n;
int lastk[maxn][maxn][maxn],lastj[maxn][maxn][maxn];
double min(double a,double b){
    return a<b?a:b;
}
int main(){
    int i,j,k,kx,jx;
    scanf("%lf%lf%d%d",&k1,&k2,&m,&n);
    for(i=0;i<maxn;i++)
        for(j=0;j<maxn;j++)
            g[i][j]=INF;
    g[0][0]=0;
    for(i=1;i<=100;i++)
        for(j=1;j<=i;j++)
            for(k=j-1;k<i;k++)
                g[i][j]=min(g[i][j],g[k][j-1]+(i-k)*(i-k)*k2);
    for(k=0;k<=100;k++)
        for(i=0;i<=m;i++)
            for(j=0;j<=n;j++)
                f[k][i][j]=INF;
    f[0][0][0]=0,memset(lastk,0,sizeof(lastk)),memset(lastj,0,sizeof(lastj));
    for(k=1;k<=100;k++)
        for(i=1;i<=m;i++)
            for(j=1;j<=n;j++)
                for(kx=lastk[k][i-1][j];kx<k;kx++)//此处错写成for(kx=lastk[k][i][j];kx<k;kx++)
                    for(jx=lastj[k][i-1][j];jx<j;jx++)//此处错写成for(jx=lastj[k][i][j];jx<j;jx++)
                        if(f[k][i][j]>f[kx][i-1][jx]+(k-kx)*(k-kx)*k1+g[k-kx][j-jx]){
                            f[k][i][j]=f[kx][i-1][jx]+(k-kx)*(k-kx)*k1+g[k-kx][j-jx];
                            lastk[k][i][j]=kx;
                            lastj[k][i][j]=jx;
                        }
    printf("%.1lf\n",f[100][m][n]);
    return 0;
}

算法三:贪心

bzoj 1505

820 kb36 msC++/Edit764 B

洛谷   25ms / 784.00KB / 613B C++

//此文https://www.cnblogs.com/Yangrui-Blog/p/9858877.html思路不错,摘抄如下:
/*

*/

//此文https://www.cnblogs.com/datam-cy/archive/2012/06/12/NOI2004-hut.html代码写得真不赖.

//提交
/*
/tmp/tmpllt9fr8z/src:18:1: 致命错误:关闭 ./cck8XUBE.s 时出错:设备上没有空间 } ^ 编译中断。
*/
//排查for(i=(m-n%m)*(n/m);i<=100-(n%m)*(n/m+1);i++){//此处错写成for(i=m-n%m;i<=100-n%m;i++){
//样例通过,提交AC.2019-10-4 19:03
//自我感觉,for(i=m-n%m;i<=100-n%m;i++){,再次提交,发现是luogu抽风,现在能过了.2019-10-4 19:08
#include <stdio.h>
double area(int block,int len,double k){//block=block-len%block块长为len/block+(len%block)块长为len/block+1的矩形
    return (len/block)*(len/block)*k*(block-len%block)+(len/block+1)*(len/block+1)*k*(len%block);
}
int main(){
    double k1,k2,ans=1e20,t;
    int m,n,i;
    scanf("%lf%lf%d%d",&k1,&k2,&m,&n);
    if(n%m==0)ans=area(m,100,k1)+area(n,100,k2);
    else
        for(i=(m-n%m)*(n/m);i<=100-(n%m)*(n/m+1);i++){//此处错写成for(i=m-n%m;i<=100-n%m;i++){
            t=area(m-n%m,i,k1)+area(n%m,100-i,k1)+area((m-n%m)*(n/m),i,k2)+area((n%m)*(n/m+1),100-i,k2);
            if(ans>t)ans=t;
            else break;
        }
    printf("%.1lf\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值