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<=i与p<=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思路不错,摘抄如下:
/*
方程的单调性优化:
从决策点len、p出发。
从面积公式可以知道,对于一段长度的草坪,越均分面积约小。注意到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 kb | 36 ms | C++/Edit | 764 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;
}