链接
http://poj.org/problem?id=3124
题解
水平低啊,自己想不出来啊
要尽可能地利用题目里隐含地条件来解题,这道题其实暗含一些条件
首先,最高的书肯定所在的书架的高度就等于这本书的高度,也就是说第一层的高度已经确定了
其次,三层的宽度之和是定值,等于所有书的宽度和
也就是说,如果我枚举其中两层的宽度,第三层的宽度就算出来了,
那么我枚举第二层和第三层的宽度,第一层的宽度就知道了
假设第二层第三层的宽度分别为
j,k
j
,
k
,那么第一层的宽度就等于
W−j−k
W
−
j
−
k
,
W
W
表示所有书的宽度之和,书架的宽度就等于三层宽度的最大值
要使宽度乘以高度和最小,就要使高度和最小,第一层的高度已知,所以就是使二三两层的高度之和最小
现在问题就成了,我知道了三层的宽度分别是多少,要求一种摆放方式使得第二第三层的高度和最小
首先想到的是:能不能贪心?仔细思考之后,发现这似乎无法贪心,而类似于一个背包问题,因为你要把每本书装进三个背包中的一个,直接贪心的话,在每一层的尾端可能无法处理
现在考虑前本书已经装好,假设我记录了
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
表示已经装进了前
i
i
本书,第二层宽度为,第三层宽度为
k
k
,二三两层的最小高度之和,那么考虑我装入第本书,由于我不知道之前怎么装的,也就无法确定这本书和已经装入的书的大小关系,所以我无法确定这本书装进去之后该不该更新答案、怎么更新
那就自然想到排个序,按照高度递减给书排个序,这样如果之前一层书架里面有书,后来装的就不会对书架的高度造成影响
那么继续想转移,如果我想把它装进第一层,就是
f[i][j][k]=f[i−1][j][k]
f
[
i
]
[
j
]
[
k
]
=
f
[
i
−
1
]
[
j
]
[
k
]
,
如果装进第二层,就要分类讨论,如果之前这一层没有书,那么放进去的这本书就会决定这一层的高度,否则这层的高度就不变,第三层同理
for(i=1 to N)for(j=0 to W)for(k=0 to W)
{
f[i][j][k]=f[i-1][j][k];
if(bk[i].w<j)f[i][j][k]=min(f[i][j][k],f[i-1][j-bk[i].w][k]);
if(bk[i].w==j)f[i][j][k]=min(f[i][j][k],f[i-1][0][k]+bk[i].h);
if(bk[i].w<k)f[i][j][k]=min(f[i][j][k],f[i-1][j][k-bk[i].w]);
if(bk[i].w==k)f[i][j][k]=min(f[i][j][k],f[i-1][j][0]+bk[i].h);
}
数组开不下,要省掉第一维
只这样还是过不了的,需要一点特技来优化
显然我们想要优化j和k的枚举范围,原先是枚举到所有书的宽度和
W
W
但是实际上不可能有这样的方案,至少每层有一本书
我现在试图找到一个面积的上界,然后找一个高度的下界,两者相除找到一个宽度的上界
都知道正方形面积最大,这里也适用,因为书的宽度,尽量平均一点放,可以让任意两行的宽度之差小于
60
60
(可以用假设法证明)
这样得到一个比实际宽度大一点的数
W3+60
W
3
+
60
,高度假设都是最高的书的高度
H
H
,那么这个面积上界就是
然后找一个高度的极小值,我们希望这个极小值尽量大一点,假设最高的书高度为
H
H
,通过原题的数据范围,发现高度不小于,那就假设最矮的书都是
150
150
,三层的高度之和的比较大的极小值就是
H+300
H
+
300
,
因此构造出一个
j,k
j
,
k
的上界
M=S/(H+300)
M
=
S
/
(
H
+
300
)
到这里就不超时了
思想提取
要善于利用隐含条件来去掉一维,以降低思维难度,比如某些和为定值
代码
//DP
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 100
#define cl(x) memset(x,0,sizeof(x))
#define iinf 0x3f3f3f3f
#define fil(x) memset(x,iinf,sizeof(x))
using namespace std;
int N, f[2110][2110];
struct book
{
int h, w;
}bk[maxn];
bool operator<(book b1, book b2){return b1.h>b2.h;}
void init()
{
fil(f), cl(bk);
int i;
scanf("%d",&N);
for(i=1;i<=N;i++)scanf("%d%d",&bk[i].h,&bk[i].w);
sort(bk+1,bk+N+1);
}
int max(int a, int b, int c)
{
return max(max(a,b),c);
}
void dp()
{
int i, j, k, ans=iinf, W=0, M;
for(i=1;i<=N;i++)W+=bk[i].w;
M=(W/3.0+60)*(3*bk[1].h)/(2*bk[1].h);
for(j=0;j<=M;j++)for(k=0;k<=M;k++)f[i][j]=iinf;
f[0][0]=0;
for(i=2;i<=N;i++)for(j=M;j>=0;j--)for(k=M;k>=0;k--)
{
if(bk[i].w<j)f[j][k]=min(f[j][k],f[j-bk[i].w][k]);
if(bk[i].w==j)f[j][k]=min(f[j][k],f[0][k]+bk[i].h);
if(bk[i].w<k)f[j][k]=min(f[j][k],f[j][k-bk[i].w]);
if(bk[i].w==k)f[j][k]=min(f[j][k],f[j][0]+bk[i].h);
}
for(j=0;j<=M;j++)for(k=0;k<=M;k++)if(f[j][k]<iinf and j and k)ans=min(ans,max(j,k,W-j-k)*(f[j][k]+bk[1].h));
printf("%d\n",ans);
}
int main()
{
int T;
for(scanf("%d",&T);T--;)init(), dp();
return 0;
}