背包思想,书有宽度,书也有高度,这里宽度是典型的体积。
因为分三层,为了方便考虑,给与约制:第一层的高度>第二层>第三层。(将书按从大到小的顺序排列)
接下来进行分阶段的动态规划,
每本书有三种选择:放在第一层,放在第二层,放在第三层。
dp[2][v2][v3],滚动数组,v2表示第二层的数宽度和,v3表示第三层的书宽度和。
因为所有书的宽度和一定,我们可以方便的计算出第一层的宽度和。
dp表示的是对应情况下书架的最小高度。
约制在状态转移的时候起到了不小的作用。
/**==========================================
* This is a solution for ACM/ICPC problem
*
* @source:uva 12099 The Bookcase
* @type: dp
* @author: wust_ysk
* @blog: http://blog.csdn.net/yskyskyer123
* @email: 2530094312@qq.com
*===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 70 ;
const int maxV= 2100 ;
#define REP(i,n) for(int i=0 ;i<(n) ;i++)
int dp[2][maxV+5][maxV+5],tot;
int n;
struct Book
{
int h,w;
bool operator<(const Book y) const
{
return h>y.h;
}
} a[maxn+3];
void update(int &ans ,int val)
{
if(ans<0||val<ans) ans=val;
}
int getwidth(int v2,int v3)
{
int ans=max(v2,v3);
return ans=max(ans,tot-v2-v3);
}
void work()
{
sort(a,a+n);
memset(dp,-1,sizeof dp);
int now=0,nex=1;
dp[now][0][0]=a[0].h;
int sum=0;
for(int i=1;i<n;i++)
{
int & w=a[i].w;
int & h=a[i].h;
for(int v2=0;v2<=sum;v2++)
{
for(int v3=0;v3<=sum-v2;v3++) if(~dp[now][v2][v3])
{
update(dp[nex][v2][v3],dp[now][v2][v3] );
update(dp[nex][v2+w][v3],v2?dp[now][v2][v3]:dp[now][v2][v3]+h );
if(v2) update(dp[nex][v2][v3+w],v3?dp[now][v2][v3]:dp[now][v2][v3]+h);
}
}
sum+=a[i].w;
memset(dp[now],-1,sizeof dp[now]);
now^=1,nex^=1;
}
int ans=INF;
for(int v2=0;v2<=tot;v2++)
{
for(int v3=0;v3<=tot-v2;v3++) if(~dp[now][v2][v3]&&v3)
{
ans=min(ans, dp[now][v2][v3]*getwidth(v2,v3) );
}
}
printf("%d\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
tot=0;
REP(i,n)
{
scanf("%d%d",&a[i].h,&a[i].w);
tot+=a[i].w;
}
work();
}
return 0;
}