题意:给你n(3<=n<=70)本书,每本书有一个高度h和宽度t,你要构造一个三层的书架,把所有书都放上,使
书架的总高度*宽度最大的那一层的宽度
最小。(即 (∑3 j=1 maxi∈Sj hi) ×(max3 j=1∑i∈Sj ti) is minimized)
思路:DP。
这里参考了网上的一种十分巧妙的dp方法,仅需要二维dp: dp[j][k]表示第二层宽度为j,第三层宽度为k的最小总高度(这两层的高度),若预处理总宽度的和sum,那剩下的那一层的宽度就是sum-j-k。我们假设第一层放高度最大的那本书,(如果不是,那我们就把放高度最高的那本书的那一层默认为第一层,这对结果是无影响的)
然后状态转移方程便十分简单了:(h表示高度,w表示宽度)
if(j>a[i].w)dp[j][k]=min(dp[j][k],dp[j-a[i].w][k]);
if(j==a[i].w)dp[j][k]=min(dp[j][k],dp[0][k]+a[i].h);
if(k>a[i].w)dp[j][k]=min(dp[j][k],dp[j][k-a[i].w]);
if(k==a[i].w)dp[j][k]=min(dp[j][k],dp[j][0]+a[i].h);
注意方程成立的前提是书的高度递减,所以要先排一下序,最后统计一下最小值即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2110;
int n,m,k;
int ans,tmp,cnt;
int dp[maxn][maxn];
struct node
{
int h,w;
bool operator<(node aa)const
{
return h>aa.h;
}
}a[maxn];
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
tmp=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].h,&a[i].w);
tmp+=a[i].w;
}
sort(a+1,a+1+n);
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=2;i<=n;i++)
{
for(int j=tmp;j>=0;j--)
for(int k=tmp;k>=0;k--)
{
if(j>a[i].w)dp[j][k]=min(dp[j][k],dp[j-a[i].w][k]);
if(j==a[i].w)dp[j][k]=min(dp[j][k],dp[0][k]+a[i].h);
if(k>a[i].w)dp[j][k]=min(dp[j][k],dp[j][k-a[i].w]);
if(k==a[i].w)dp[j][k]=min(dp[j][k],dp[j][0]+a[i].h);
}
}
ans=inf;
for(int i=tmp;i>0;i--)
for(int j=tmp;j>0;j--)
if(dp[i][j]<=900) ans=min(ans,(dp[i][j]+a[1].h)*max(max(i,j),tmp-i-j));
printf("%d\n",ans);
}
return 0;
}