UVA - 12099 The Bookcase(巧妙DP)

题意:给你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;
}

猜你喜欢

转载自blog.csdn.net/lsd20164388/article/details/80670803
今日推荐