【UVa】【DP】12099 Bookcase

版权声明:本文为lzr原创文章,欢迎大家转载,如需转载请注明:By CQBZ LZR https://blog.csdn.net/qq_37656398/article/details/81946910

UVa 12099 Bookcase

题目

◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆

题目大意

N 本书,第 i 本书有一个高度 H i 和宽度 W i ,现要求构建一个三层的书架,你必须把所有书放在书架上。设三层高度(该层最高的书的高度)之和为 h ,书架总宽度(即每层总宽度的最大值)为 w ,要求 h × w 尽可能小。

思路

首先我们可以考虑将所有书按高度从大到小排序。不妨设最高的书在第1层,且第二层的高度大于等于第三层的高度。

则我们可以定义状态 d ( i , j , k ) 为当前已经安排了 i 本书,第二层的宽度为 j ,第三层的宽度为 k 时第二层和第三层的高度之和。

为什么不记录第一层的高度?因为最高的书在第一层,即这一层的书永远都不会比它更高了。

为什么不记录第一层的宽度?因为目前三层的书的总宽度为前 i 本书的总宽度,所以只要知道了第二、第三层的宽度,就知道了第一层的宽度。

另外,因为这些书已经从大到小排了序,所以,一旦三层都放了书,则书架的总高度就已经确定了。

综上,我们可以得到以下两点:

  • 若只有第一、第二层放了书,则当且仅当往第三层放书 i 时,第三层的高度会变为 H i
  • 若只有第一层放了书,则当且仅当第二层放书 i 时,第二层的高度会变为 H i

所以我们采用刷表法进行转移。

将书 i 放在第一层:用 d ( i , j , k ) 更新 d ( i + 1 , j , k ) ,因为第一层的高度不变。

将书 i 放在第二层:用 d ( i , j , k ) + f ( j , H i ) 更新 d ( i + 1 , j + W i , k )

将书 i 放在第三层:用 d ( i , j , k ) + f ( k , H i ) 更新 d ( i + 1 , j , k + W i )

其中: f ( j , H i ) 当且仅当 j = 0 时等于 H i ,否则等于 0

注意:由于状态数量很大( 70 × 2100 × 2100 ),所以我们必须采用滚动数组进行递推。

正解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int Maxn=70;
const int Maxw=30;
const int INF=0x3f3f3f3f;

struct Book {
    int h,w;
    bool operator < (const Book rhs) const {
        return h>rhs.h||(h==rhs.h&&w>rhs.w);
    }
}A[Maxn+5];
int N,sumw[Maxn+5];
int dp[2][Maxn*Maxw+5][Maxn*Maxw+5];

inline int f(int w,int h) {
    return w==0?h:0;
}
inline void update(int &newd,int d) {
    if(newd<0||d<newd)newd=d;
}

void Prepare() {
    sort(A+1,A+N+1);
    sumw[1]=0;
    for(int i=1;i<=N+1;i++)
        sumw[i]=sumw[i-1]+A[i-1].w;
}

int main() {
    #ifdef LOACL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    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);
        Prepare();
        int t=0;
        for(int i=1;i<=N;i++) {
            for(int j=0;j<=sumw[i+1];j++)
                for(int k=0;k<=sumw[i+1]-j;k++)
                    dp[t^1][j][k]=-1;
            //不要使用memset,它太慢了!
            for(int j=0;j<=sumw[i];j++)
                for(int k=0;k<=sumw[i]-j;k++)
                    if(dp[t][j][k]>=0) {
                        update(dp[t^1][j][k],dp[t][j][k]);
                        update(dp[t^1][j+A[i].w][k],dp[t][j][k]+f(j,A[i].h));
                        update(dp[t^1][j][k+A[i].w],dp[t][j][k]+f(k,A[i].h));
                    }
            t^=1;
        }
        int ans=INF;
        for(int j=1;j<=sumw[N+1];j++)
            for(int k=1;k<=sumw[N+1]-j;k++)
                if(dp[t][j][k]>=0) {
                    int w=max(max(j,k),sumw[N+1]-j-k);
                    int h=A[1].h+dp[t][j][k];
                    ans=min(ans,h*w);
                }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37656398/article/details/81946910