本题采用了dp+加状态剪枝的策略;
第一点,必须明确,最优子结构为前面 i 本书的最佳放法是由 前 i-1本书的最佳方法的基础上加上第 i 本书组合而来;
d[i ][j ][k ] 代表已经安置 前 i本书,第二层宽度为j,第三层宽度为k ,且第二层高>=第三层高度,最高的那本书放在第一层时的 第二层和第三层的最小高度和;
该状态是在每层厚度一定情况下的最优解;
这样一来最终解要遍历i = n的所有状态求最优;
由于d[i ][j ][k ] 并不能明显的找出其所依赖的子结构,但用它来更新i+1的状态却比较容易转移,所以采用刷表法
还有状态太大需要剪剪枝,见代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 2110000
#define Inf 1000
const int maxn = 71;
const int maxm = 2105;
int d[maxn][maxm][maxm],n,maxw=30,sumw[maxn];
struct Book{
int H,W;
bool operator < (const Book& rhs)const{
return H>rhs.H;
}
}a[maxn];
int f(int i,int j){
return i==0 ? j : 0;
}
long long dp(){
int lim=n*maxw;
for(int i=1;i<=n;i++){
for(int j=0;j<=lim;j++)
for(int k=0;k<=lim;k++){
if(j+k>sumw[i]-a[1].W || sumw[i]-j-k + 30 < j || j + 30 < k) break;
d[i][j][k]=Inf;
}
}
d[1][0][0]=0;
int ans = INF;
for(int i=1;i<n;i++){
for(int j=0;j<=lim;j++)
for(int k=0;k<=lim;k++){
if(j+k>sumw[i]-a[1].W || sumw[i]-j-k + 30 < j || j + 30 < k) break;
d[i+1][j][k]=min(d[i+1][j][k],d[i][j][k]);
d[i+1][j+a[i+1].W][k] = min(d[i+1][j+a[i+1].W][k],d[i][j][k] + f(j,a[i+1].H));
if(j>0)
d[i+1][j][k+a[i+1].W] = min(d[i+1][j][k+a[i+1].W],d[i][j][k] + f(k,a[i+1].H));
}
}
for(int j=0;j<=lim;j++)
for(int k=0;k<=lim;k++){
if(j+k>sumw[n]-a[1].W || sumw[n]-j-k + 30 < j || j + 30 < k) break;
if(d[n][j][k]!=INF&&j>0&&k>0){
ans = min(ans,(d[n][j][k]+a[1].H)*(max(sumw[n]-j-k,max(j,k))));
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d %d",&a[i].H,&a[i].W);
sort(a+1,a+1+n);
sumw[0]=0;
for(int i=1;i<=n;i++){
sumw[i]=a[i].W+sumw[i-1];
}
printf("%d\n",dp());
}
return 0;
}