poj3124 The Bookcase

版权声明:转载请注明出处 https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/82027513

链接

http://poj.org/problem?id=3124

题解

水平低啊,自己想不出来啊
要尽可能地利用题目里隐含地条件来解题,这道题其实暗含一些条件
首先,最高的书肯定所在的书架的高度就等于这本书的高度,也就是说第一层的高度已经确定了
其次,三层的宽度之和是定值,等于所有书的宽度和
也就是说,如果我枚举其中两层的宽度,第三层的宽度就算出来了,
那么我枚举第二层和第三层的宽度,第一层的宽度就知道了
假设第二层第三层的宽度分别为 j , k ,那么第一层的宽度就等于 W j k W 表示所有书的宽度之和,书架的宽度就等于三层宽度的最大值
要使宽度乘以高度和最小,就要使高度和最小,第一层的高度已知,所以就是使二三两层的高度之和最小
现在问题就成了,我知道了三层的宽度分别是多少,要求一种摆放方式使得第二第三层的高度和最小
首先想到的是:能不能贪心?仔细思考之后,发现这似乎无法贪心,而类似于一个背包问题,因为你要把每本书装进三个背包中的一个,直接贪心的话,在每一层的尾端可能无法处理
现在考虑前 i 1 本书已经装好,假设我记录了 f [ i ] [ j ] [ k ] 表示已经装进了前 i 本书,第二层宽度为 j ,第三层宽度为 k ,二三两层的最小高度之和,那么考虑我装入第 i 本书,由于我不知道之前怎么装的,也就无法确定这本书和已经装入的书的大小关系,所以我无法确定这本书装进去之后该不该更新答案、怎么更新
那就自然想到排个序,按照高度递减给书排个序,这样如果之前一层书架里面有书,后来装的就不会对书架的高度造成影响
那么继续想转移,如果我想把它装进第一层,就是 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
但是实际上不可能有这样的方案,至少每层有一本书
我现在试图找到一个面积的上界,然后找一个高度的下界,两者相除找到一个宽度的上界
都知道正方形面积最大,这里也适用,因为书的宽度 30 ,尽量平均一点放,可以让任意两行的宽度之差小于 60 (可以用假设法证明)
这样得到一个比实际宽度大一点的数 W 3 + 60 ,高度假设都是最高的书的高度 H ,那么这个面积上界就是 S = H ( W 3 + 60 )
然后找一个高度的极小值,我们希望这个极小值尽量大一点,假设最高的书高度为 H ,通过原题的数据范围,发现高度不小于 150 ,那就假设最矮的书都是 150 ,三层的高度之和的比较大的极小值就是 H + 300
因此构造出一个 j , k 的上界 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;
}

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/82027513
今日推荐