题目链接:点击打开链接
用O(n(sum(t)^2))的方法过的心惊胆战。。1800+ms惊险AC。。幸好AC了要是TLE了还得打回去重想。。
思路是直接枚举各层宽度,由于书的总宽度固定所有只需要记录下面两层的宽度即可。
设dp[k][i][j]为当前书架放了前k本书,第一层宽度为i,第二层宽度为j时的最低高度。
为了避免不断去比较高度,先将所有书按从高到低排序,那么只需在空的层中放入书时需要加上书的高度之外,每次放入新的书都无需再更新高度(因为下一本书必定比同层的书高度低,故不会影响到该层的最高高度)。
状态转移也很好想,每本书只有放第一层、放第二层、放第三层这三种决策,去找对应的状态就行了。
需要注意最终状态不允许有哪层是空的。
感觉这种方法还是有点拼rp的,不知道有没有更优的方法。
代码如下:
#include<bits/stdc++.h>
#define INF 1000000000
using namespace std;
struct node
{
int h,w;
bool operator <(const node &p)const
{
return h>p.h;
}
};
node a[105];
int dp[2][2105][2105];
int n;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int SW=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i].h,&a[i].w);
SW+=a[i].w;
}
sort(a,a+n);
for(int i=0;i<=SW;i++)
for(int j=0;j<=SW;j++)
dp[0][i][j]=INF;
dp[0][0][0]=0;
int cur=1;
int SumW=0;
for(int k=0;k<n;k++)
{
SumW+=a[k].w;
for(int i=0;i<=SW;i++)
for(int j=0;j<=SW;j++)
{
dp[cur][i][j]=INF;
int p=j-a[k].w;
if(p==0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][i][p]+a[k].h);
else if(p>0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][i][p]);
p=i-a[k].w;
if(p==0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][p][j]+a[k].h);
else if(p>0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][p][j]);
p=SumW-i-j-a[k].w;
if(p==0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][i][j]+a[k].h);
else if(p>0)
dp[cur][i][j]=min(dp[cur][i][j],dp[cur^1][i][j]);
// if(dp[cur][i][j]!=INF)printf("dp[%d][%d][%d]=%d\n",k,i,j,dp[cur][i][j]);
}
cur^=1;
}
cur^=1;
int ans=INF;
for(int i=1;i<=SW;i++)
for(int j=1;j<=SW;j++)
{
if(dp[cur][i][j]>=INF)continue;
if(SumW-i-j==0)continue;
int W=max(max(i,j),SumW-i-j);
ans=min(ans,dp[cur][i][j]*W);
}
printf("%d\n",ans);
}
}