本题采用了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; }