UVA - 12099 The Bookcase 书架


背包思想,书有宽度,书也有高度,这里宽度是典型的体积。

因为分三层,为了方便考虑,给与约制:第一层的高度>第二层>第三层。(将书按从大到小的顺序排列)

接下来进行分阶段的动态规划,

每本书有三种选择:放在第一层,放在第二层,放在第三层。

dp[2][v2][v3],滚动数组,v2表示第二层的数宽度和,v3表示第三层的书宽度和。

因为所有书的宽度和一定,我们可以方便的计算出第一层的宽度和。

dp表示的是对应情况下书架的最小高度。

约制在状态转移的时候起到了不小的作用。



/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source:uva 12099 The Bookcase
 *   @type:  dp
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 70   ;
const int maxV= 2100   ;
#define REP(i,n)  for(int i=0 ;i<(n) ;i++)
int dp[2][maxV+5][maxV+5],tot;
int n;
struct Book
{
    int h,w;
    bool operator<(const Book y) const
    {
        return h>y.h;
    }
} a[maxn+3];


void update(int &ans ,int val)
{
    if(ans<0||val<ans)  ans=val;
}

int getwidth(int v2,int v3)
{
    int ans=max(v2,v3);

    return ans=max(ans,tot-v2-v3);
}
void work()
{
    sort(a,a+n);
    memset(dp,-1,sizeof dp);
    int now=0,nex=1;
    dp[now][0][0]=a[0].h;
    int sum=0;
    for(int i=1;i<n;i++)
    {
        int & w=a[i].w;
        int & h=a[i].h;
        for(int v2=0;v2<=sum;v2++)
        {
            for(int v3=0;v3<=sum-v2;v3++)  if(~dp[now][v2][v3])
            {
                update(dp[nex][v2][v3],dp[now][v2][v3]   );
                update(dp[nex][v2+w][v3],v2?dp[now][v2][v3]:dp[now][v2][v3]+h  );
                if(v2)  update(dp[nex][v2][v3+w],v3?dp[now][v2][v3]:dp[now][v2][v3]+h);
            }
        }
        sum+=a[i].w;
        memset(dp[now],-1,sizeof dp[now]);
        now^=1,nex^=1;

    }
    int ans=INF;
    for(int v2=0;v2<=tot;v2++)
    {
        for(int v3=0;v3<=tot-v2;v3++)  if(~dp[now][v2][v3]&&v3)
        {
            ans=min(ans, dp[now][v2][v3]*getwidth(v2,v3) );
        }
    }
    printf("%d\n",ans);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        tot=0;
        REP(i,n)
        {
            scanf("%d%d",&a[i].h,&a[i].w);
            tot+=a[i].w;
        }
        work();
    }

   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值