思路: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; }