UVa12099书架

题意:
给你n(3<=n<=70)本书,每本书有一个高度h和宽度t,你要构造一个三层的书架,把所有书都放上,使书架的总高度*3层中的最大宽度最小。

思路:
书架有3层,每一层都有高度和宽度,状态里怎么表示呢。首先把书按照高度排序(从高到低),最高的放在第一层,以后再在第一层加书,第一层的高度也不会变了。
d[i][j][k]:安排完前 i 本书,第2层书的宽度为j,第3层书的宽度为k时,第2,3层书架的高度之和最小值。
这样整个书架的高度 = 第1层高度 + 某个状态值。
整个书架的宽度 = max( j , k , sumw[n] - j - k )。sumw[n]是前n本书的总宽度。

如果放入某一层之前,这一层是空,那么这一层的高度从0->hi。
若不为空,则放入的这本书对本层无影响(高度已经排序,后面的总比前面小),高度+0;
可以用一个 f 函数来表示这种增长,f( 0, h) = h,其余情况为0。

inline int f(int w, int h){
	return w == 0 ? h : 0;
}

决策一共3中,将当前书放到第1 或 2 或 3层,用刷表法转移:

  1. 若放入第1层,用d( i , j , k ) 更新d( i+1, j ,k)。因为第2,3层高度不变。
  2. 若放入第2层,用d(i , j , k) + f( j, hi) 更新 d( i+1, j + wi, k )。
  3. 若放入第3层,用d(i , j , k) + f( k, hi) 更新 d( i+1, j, k + wi )。

优化:
4. j + k < 前i本书的宽度总和。
5. 若上一层宽度和 - 下一场宽度和 > 30(一本书的宽度最大值),那么这本书可以放到下一层,这样既不会增加整个书架的宽度,也不增加高度。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1<<30;
const int maxn = 70;
const int maxw = 30;

struct Book{
	int h,w;
	bool operator < (const Book &rhs) const{ 
		return h > rhs.h||(h == rhs.h&&w > rhs.w);
	}
};
Book books[maxn];
int d[2][maxn*maxw][maxn*maxw]; // 滚动数组 
int sumw[maxn];

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;
}

int main()
{
	//freopen("in.txt","r",stdin);
	int T; scanf("%d",&T);
	while(T--){
		int n;
		scanf("%d",&n);
		//int sumw = 0;
		for(int i = 0; i < n; ++i) {
			scanf("%d%d",&books[i].h,&books[i].w);
			//sumw += books[i].w;
		}
		
		sort(books, books + n);
		
		sumw[0] = 0;
		for(int i = 1; i <= n; ++i){
			sumw[i] = sumw[i-1] + books[i-1].w;
		}
		 
		//memset(d,-1,sizeof(d)); 太慢 
		
		d[0][0][0] = 0;
		int t = 0;
		for(int i = 0; i < n; ++i){
			// 初始化 
			for(int j = 0; j <= sumw[i+1]; ++j)
				for(int k = 0; k <= sumw[i+1] - j; ++k) d[t^1][j][k] = -1;
      
			for(int j = 0; j <= sumw[i]; ++j){
				for(int k = 0; k <= sumw[i]-j; ++k){
					int w1 = sumw[i] - j - k;
					if(d[t][j][k] >= 0&&(j <= w1+30||j <= k+30)){
						update(d[t^1][j][k], d[t][j][k]); // 放在第1层 
						update(d[t^1][j+books[i].w][k], d[t][j][k] + f(j, books[i].h)); // 第2层 
						update(d[t^1][j][k+books[i].w], d[t][j][k] + f(k, books[i].h)); // 第3层 
					}
				}
			}
			t ^= 1; 
		}
		 
		int h1 = books[0].h; // 最高的书放在第一层 
		int ans = INF;
		for(int j = 1; j <= sumw[n]; ++j){ // 至少一本书,宽度不为0 
			for(int k = 1; k <= sumw[n]-j; ++k){
				int w1 = sumw[n] - j - k;
				if(d[t][j][k] >= 0&&(j <= w1+30||j <= k+30)){
					int w = max(max(j,k), w1); // 最大宽度 
					int h = h1 + d[t][j][k]; // 总高度 
					ans = min(ans, w*h);
				}
			} 
		} 
		printf("%d\n",ans);
	}

	return 0;
}


猜你喜欢

转载自blog.csdn.net/CY05627/article/details/88775411