BZOJ 1933 [Shoi2007] Bookcase 书柜的尺寸

216 篇文章 0 订阅
160 篇文章 0 订阅

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DP+思路~

求最大高、之和,和最大、宽之和的乘积的最小值~(注意断句!)

首先,我们可以用f[i][j][k][z]表示目前DP到第i本书,三个柜的书的宽度分别为j,k,z的最小的最大高的值,但是显然像这样所有维度都记录的话会MLE,所以我们只能记录一部分的状态。

注意到DP到i时,已有的书的宽度之和是一定的,所以我们只需要记录三块中的两块的宽度之和就可以了。而i对递推过程无影响,所以把i维换成滚动形式。

所以用f[kkz][j][k]表示目前用kkz^1更新kkz,三个书柜宽度分别为j,k,sum[i-1]-j-k的最小的最大高度,然后预处理出宽度的前缀和sum[i],直接DP即可~


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

int n,f[2][2101][2101],inf,kkz,sum[71],h,w,ans;

struct node{
	int h,w;
}a[71];

bool operator <(node u,node v)
{
	return u.h>v.h;
}

int read()
{
	int totnum=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {totnum=(totnum<<1)+(totnum<<3)+ch-'0';ch=getchar();}
	return totnum*f;
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i].h=read(),a[i].w=read();
	memset(f[kkz],127/3,sizeof(f[kkz]));ans=inf=f[kkz][0][0];f[kkz][0][0]=0;
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i].w;
	for(int i=1;i<=n;i++)
	{
		kkz^=1;h=a[i].h;w=a[i].w;
		memset(f[kkz],127/3,sizeof(f[kkz]));
		for(int j=sum[i-1];~j;j--)
		  for(int k=sum[i-1];~k;k--)
		    if(j+k<=sum[i-1] && f[kkz^1][j][k]<inf)
		    {
		    	if(j) f[kkz][j+w][k]=min(f[kkz][j+w][k],f[kkz^1][j][k]);
		    	else f[kkz][w][k]=min(f[kkz][j][k],f[kkz^1][j][k]+h);
		    	if(k) f[kkz][j][k+w]=min(f[kkz][j][k+w],f[kkz^1][j][k]);
		    	else f[kkz][j][w]=min(f[kkz][j][w],f[kkz^1][j][k]+h);
		    	if(j+k<sum[i-1]) f[kkz][j][k]=min(f[kkz][j][k],f[kkz^1][j][k]);
		    	else f[kkz][j][k]=min(f[kkz][j][k],f[kkz^1][j][k]+h);
		    }
	}
	for(int i=1;i<sum[n];i++)
	  for(int j=1;j<sum[n];j++)
	    if(i+j<sum[n] && f[kkz][i][j]<inf)
	      ans=min(ans,max(max(i,j),sum[n]-i-j)*f[kkz][i][j]);
	printf("%d\n",ans);
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值