bzoj 1933: [Shoi2007]Bookcase 书柜的尺寸

1933: [Shoi2007]Bookcase 书柜的尺寸

Time Limit: 5 Sec Memory Limit: 64 MB
Submit: 557 Solved: 214
[Submit][Status][Discuss]
Description

Tom不喜欢那种一字长龙式的大书架,他只想要一个小书柜来存放他的系列工具书。Tom打算把书柜放在桌子的后面,这样需要查书的时候就可以不用起身离开了。显然,这种书柜不能太大,Tom希望它的体积越小越好。另外,出于他的审美要求,他只想要一个三层的书柜。为了物尽其用,Tom规定每层必须至少放一本书。现在的问题是,Tom怎么分配他的工具书,才能让木匠造出最小的书柜来呢? Tom很快意识到这是一个数学问题。每本书都有自己的高度hi和厚度ti。我们需要求的是一个分配方案,也就是要求把所有的书分配在S1、S2和S3三个非空集合里面的一个,不重复也不遗漏,那么,很明显,书柜正面表面积(S)的计算公式就是: 由于书柜的深度是固定的(显然,它应该等于那本最宽的书的长度),所以要求书柜的体积最小就是要求S最小。Tom离答案只有一步之遥了。不过很遗憾,Tom并不擅长于编程,于是他邀请你来帮助他解决这个问题。
Input

文件的第一行只有一个整数n(3≤n≤70),代表书本的本数。接下来有n行,每行有两个整数hi和ti,代表每本书的高度和厚度,我们保证150≤hi≤300,5≤ti≤30。
Output

只有一行,即输出最小的S。
Sample Input

4

220 29

195 20

200 9

180 30

Sample Output

18000
HINT

Source

Day2


【分析】
不禁感觉自己略有点zz
这个题思路有点奇怪啊…用dp[p][i][j]表示前p本书中,第一层放书的总厚度为i,第二层放书的总厚度为j时的书架最小总高度orz
然后第一层p数组滚一下(ノ`Д)ノ…
注意dp的时候滚动数组要初始化嗷嗷嗷嗷检查了半个小时嗷(ノ`Д)ノ


【代码】

//bzoj 1933: [Shoi2007]Bookcase 书柜的尺寸
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mx=75;
int n,m;
ll ans=1e9; 
int sum[mx];
int dp[2][2105][2105];   //第一层厚度j,第二层厚度k,第三层厚度sum-j-k的最小总高度 
struct book {int h,t;} a[mx]; 
inline bool comp(const book &x,const book &y) {return x.h>y.h;}
int main()
{
    int i,j,k,p,now,pre,up,inf;
    scanf("%d",&n);
    fo(i,1,n)
      scanf("%d%d",&a[i].h,&a[i].t);
    sort(a+1,a+n+1,comp);
    memset(dp,0x3f,sizeof dp);
    inf=dp[0][0][0];
    dp[1][0][0]=dp[0][0][0]=0;
    fo(i,1,n) sum[i]=sum[i-1]+a[i].t;
    up=sum[n]; //上界
    fo(p,1,n)
    {
        now=p&1,pre=now^1;
        memset(dp[now],0x3f,sizeof dp[now]);
        fo(i,0,sum[p]) 
          fo(j,0,sum[p])
            if(dp[pre][i][j]!=inf)
            {
                if(i!=0) dp[now][i+a[p].t][j]=min(dp[now][i+a[p].t][j],dp[pre][i][j]);
                else dp[now][i+a[p].t][j]=min(dp[now][i+a[p].t][j],dp[pre][i][j]+a[p].h);

                if(j!=0) dp[now][i][j+a[p].t]=min(dp[now][i][j+a[p].t],dp[pre][i][j]);
                else dp[now][i][j+a[p].t]=min(dp[now][i][j+a[p].t],dp[pre][i][j]+a[p].h);

                if(sum[p-1]-i-j!=0) dp[now][i][j]=min(dp[now][i][j],dp[pre][i][j]);
                else dp[now][i][j]=min(dp[now][i][j],dp[pre][i][j]+a[p].h);
            }
    }
    fo(i,1,up)
      fo(j,1,up)
        if(up-i-j)
            ans=min(ans,(ll)max(up-i-j,max(i,j))*(ll)dp[now][i][j]);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值