12099 - The Bookcase

No wonder the old bookcase caved under the
massive piles of books Tom had stacked on it.
He had better build a new one, this time large
enough to hold all of his books. Tom nds it
practical to have the books close at hand when
he works at his desk. Therefore, he is imagining
a compact solution with the bookcase standing
on the back of the desk. Obviously, this would
put some restrictions on the size of the bookcase,
it should preferably be as small as possible. In
addition, Tom would like the bookcase to have
exactly three shelves for aesthetical reasons.
Wondering how small his bookcase could be,
he models the problem as follows. He measures
the height hi and thickness ti of each book i and
he seeks a partition of the books in three non-empty sets S1, S2, S3 such that (Σ3
j=1 maxi2Sjhi)
(max3
j=1Σi2Sj
ti) is minimized, i.e. the area of the bookcase as seen when standing in front of it
(the depth needed is obviously the largest width of all his books, regardless of the partition). Note
that this formula does not give the exact area of the bookcase, since the actual shelves cause a small
additional height, and the sides cause a small additional width. For simplicity, we will ignore this small
discrepancy.
Thinking a moment on the problem, Tom realizes he will need a computer program to do the job.
Input
The input begins with a positive number on a line of its own telling the number of test cases (at
most 20). For each test case there is one line containing a single positive integer N, 3 N 70
giving the number of books. Then N lines follow each containing two positive integers hi, ti, satisfying
150 hi 300 and 5 ti 30, the height and thickness of book i respectively, in millimeters.
Output
For each test case, output one line containing the minimum area (height times width) of a three-shelf
bookcase capable of holding all the books, expressed in square millimeters.
Sample Input
2
4
220 29
195 20
200 9
180 30
6
256 20
255 30
254 15
253 20
252 15
251 9
Sample Output
18000
29796

我有话说:
这道题初看时并没有什么好办法。但紫书中说要先按照书的高度排序。这是一个很大的优化。因为这样在每一层在放上第一本书之后的后续处理中不需要考虑高度的变化。下面是状态的定义:
我们先把第一本书放在第一层。定义dp(i,j,k)为已处理i本书,在第二层上书的总宽度为j,第三层书的总宽度为k时。第二三两层书的最小总高度。其实我们已经知道三层书的总宽度为dp(i,j,k)+h[0].,所有书的宽度是一定的,那么第一层的宽度为
转移策略无非有三:
一、放在第一层。那么dp(i,j,k)可以向dp(i,j,k)转移;
二、放在第二层。那么dp(i+1,j+w[i],k)=min{dp(i+1,j+w[i],k),dp(i,j,k)+f(j,h[i])转移。f(j,h[i])当j=0时为h[i]否则为0;
三、放在第三层。大致和二类似。
另外的。还有优化,否则状态总数70*2100*2100难以让人接受。
滚动数组,缩小空间。
优化1.注意到j+k应不大于前i本书的总宽度。
优化2.如果以ww1代表第一层总宽度。如果ww1+30 < ww2,30是一本书最大宽度。那么完全可以把第二层的一本书移到第一层。宽度肯定不会增加。换一句话说。我们只要解决满足ww2+30<=ww1;ww3+30<=ww2的状态就可以了。
优化1:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn=70+5;
const int maxw=30;
const int INF=1000000000;
struct Book{
   int h,w;
   bool operator < (const Book& rhs)const{
       return h>rhs.h||(h==rhs.h&&w>rhs.w);
   }
}books[maxn];
int n,dp[2][maxn*maxw][maxn*maxw],sumw[maxn];
inline int f(int w,int h)
{
    return w==0?h:0;
}
inline void update(int& newd,int d)
{
    if(newd<0||newd>d)newd=d;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&books[i].h,&books[i].w);
        }
        sort(books,books+n);
        sumw[0]=0;
        for(int i=1;i<=n;i++)
            sumw[i]=sumw[i-1]+books[i-1].w;
        dp[0][0][0]=0;
        int t=0;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<=sumw[i+1];j++)
                for(int k=0;k<=sumw[i+1]-j;k++)
                    dp[t^1][j][k]=-1;
            for(int j=0;j<=sumw[i];j++)
            {
                for(int k=0;k<=sumw[i]-j;k++)
                    if(dp[t][j][k]>=0){
                       update(dp[t^1][j][k],dp[t][j][k]);
                       update(dp[t^1][j+books[i].w][k],dp[t][j][k]+f(j,books[i].h));
                       update(dp[t^1][j][k+books[i].w],dp[t][j][k]+f(k,books[i].h));
                   }
            }
            t^=1;
        }
        int ans=INF;
        for(int j=1;j<=sumw[n];j++)//每层至少有一本书,因为最少是三本,排序后,最高的放在第一层,那么,每层都放肯定是最优的。所以宽度一定不为0
        {
            for(int k=1;k<=sumw[n]-j;k++)
            {
                if(dp[t][j][k]>=0){
                    int w=max(max(j,k),sumw[n]-j-k);
                    int h=dp[t][j][k]+books[0].h;
                    ans=min(ans,w*h);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

优化1,2:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn=70+5;
const int maxw=30;
const int INF=1000000000;
struct Book{
   int h,w;
   bool operator < (const Book& rhs)const{
       return h>rhs.h||(h==rhs.h&&w>rhs.w);
   }
}books[maxn];
int n,dp[2][maxn*maxw][maxn*maxw],sumw[maxn];
inline int f(int w,int h)
{
    return w==0?h:0;
}
inline void update(int& newd,int d)
{
    if(newd<0||newd>d)newd=d;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&books[i].h,&books[i].w);
        }
        sort(books,books+n);
        sumw[0]=0;
        for(int i=1;i<=n;i++)
            sumw[i]=sumw[i-1]+books[i-1].w;
        dp[0][0][0]=0;
        int t=0;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<=sumw[i+1];j++)
                for(int k=0;k<=sumw[i+1]-j;k++)
                    dp[t^1][j][k]=-1;
            for(int j=0;j<=sumw[i];j++)
            {
                for(int k=0;k<=sumw[i]-j;k++)
                {
                    if(k>j+30||j>sumw[n]-j-k+30)continue;
                    if(dp[t][j][k]>=0){
                       update(dp[t^1][j][k],dp[t][j][k]);
                       update(dp[t^1][j+books[i].w][k],dp[t][j][k]+f(j,books[i].h));
                       update(dp[t^1][j][k+books[i].w],dp[t][j][k]+f(k,books[i].h));
                   }
                }

            }
            t^=1;
        }
        int ans=INF;
        for(int j=1;j<=sumw[n];j++)//每层至少有一本书,因为最少是三本,排序后,最高的放在第一层,那么,每层都放肯定是最优的。所以宽度一定不为0
        {
            for(int k=1;k<=sumw[n]-j;k++)
            {
                if(k>j+30||j>sumw[n]-j-k+30)continue;
                if(dp[t][j][k]>=0){
                    int w=max(max(j,k),sumw[n]-j-k);
                    int h=dp[t][j][k]+books[0].h;
                    ans=min(ans,w*h);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值