【dp&技巧】书架 UVA12099

本文介绍了一种关于书籍摆放的动态规划问题,旨在通过优化算法减少计算时间和空间复杂度。通过对书籍的高度和宽度进行合理分配,实现三层书架存放书籍时最小化总体积的目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意,n(<=70)本书,每本书有一个高度(<=150),宽度(<=30),把他们分到书架里的三层,每层的高度就是这层里所有书的最大高度,宽度就是所有书的宽度和。ans=三层高度和*三层宽度最大值,求ans最小值


又是一道ACM毒瘤好dp,最近做dp感觉出来了点规律,很多时候可以制定一些规则,使无论怎样都有最优答案都能满足这些规则,这样减少了最优答案的数量dp自然就简化了。所以在这个题中,如果求出来最优方案后,调整每一层的顺序不会改变答案,所以就定第一层高度最高,第二层其次。因为第一本书肯定要放进书架里,所以先把它放进第一层。


设状态的时候,设dp(i,j,k)为放了i本书,第二层宽度为j,第三层宽度为j,二三层高度和的最小值。这样根据j,k可以推出来第一层宽度。所以每次对于第i本书有放进三层三种决策,转移方程推一下就可以了。


这样会发现,如果把所有书放到一层,那么这一层宽度最大30*70=2100,空间用滚动数组优化,时间不优化是70*2100*2100,大数据会被卡(事实上vjudge上数据不强,最多20组测试数据跑了1180ms)

书上介绍了两个神仙优化,看完了以后我第一次知道dp还可以剪枝

1.这个还比较好想,放第i本书时j+k应小于第2本书到第i本书宽度和,这样可以在循环k时剪去一部分无用状态

2.所有书宽度和为sum,设想如果相邻两层宽度差>30,那么把一本书从这层拿到邻层,高度和不加,宽度也不加,所以我们只保留状态w1+30>=w2,w2+30>=w3,化式子可得w2<=(sum+30)/2,w3<=(sum+60)/3,这样w2最大1065,w3最大720常数小了六倍左右

这两个优化一上,常数小了六倍多,跑了180ms

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF (2100010)
#define LL long long
using namespace std;
int dp[2][1100][800],n,A[200];
struct Book{
	int w,h;
	void Read(){
		scanf("%d %d",&h,&w);
	}
	bool operator < (const Book &a) const{
		return h>a.h;
	}
}T[200];
LL Min(LL a,LL b){
	if (a<b) return a;
	return b;
}
void Work(){
	int i,j,k,sum=0,max2,max3;
	scanf("%d",&n);
	memset(A,0,sizeof(A));
	for (i=1;i<=n;i++) T[i].Read();
	sort(T+1,T+n+1);
	for (i=2;i<=n;i++) A[i]=T[i].w+A[i-1];
	sum=A[n]+T[1].w;
	
	memset(dp,127,sizeof(dp));
	max2=(sum+30)/2+10; max3=(sum+60)/3+10;
	dp[1][0][0]=0;
	for (i=2;i<=n;i++)
		for (j=0;j<=max2;j++)
			for (k=0;k+j<=A[i],k<=max3;k++){
				int &ans=dp[i&1][j][k];
				ans=INF;
				ans=min(ans,dp[(i-1)&1][j][k]);//把书放在第一层 
				if (j-T[i].w>=0)//把书放在第二层
					ans=min(ans,dp[(i-1)&1][j-T[i].w][k]+T[i].h*(j==T[i].w));
				if (k-T[i].w>=0)//把书放在第三层
					ans=min(ans,dp[(i-1)&1][j][k-T[i].w]+T[i].h*(k==T[i].w));
			}
	LL ans=210000000000000000;
	for (i=1;i<=max2;i++)
		for (j=1;j<=max3;j++)
			ans=Min(ans,(LL)max(max(i,j),sum-i-j)*(LL)(dp[n&1][i][j]+T[1].h));
	cout<<ans<<endl;			
}
int main(){
	int Case_Num;
	scanf("%d",&Case_Num);
	while (Case_Num--) Work();
	return 0;
}


### 问题分析 UVA12099 **The Bookcase** 是一个典型的动态规划问题,涉及将多个书架,要求最小化书架的总高度。问题的核心是: - 给定一组籍,每个籍具有固定的宽度和高度。 - 书架的宽度有限,籍必须按照顺序置,且每层书架的总宽度不能超过限制。 - 每层书架的高度是该层中所有籍的最大高度。 - 任务是安排籍的分布,使得书架的总高度最小化。 ### 动态规划思路 该问题可以通过动态规划求解。定义状态 `dp[i]` 表示前 `i` 本的最小总高度。 为了优化状态转移,可以使用如下策略: - 预处理计算每本到另一本之间的最大高度。 - 状态转移方程为: `dp[i] = min(dp[j] + max_height(j+1..i))`,其中 `j < i` 且 `sum_width(j+1..i) <= shelf_width`。 ### C++ 实现 ```cpp #include <iostream> #include <vector> #include <algorithm> #include <climits> using namespace std; int minTotalHeight(const vector<int>& heights, const vector<int>& widths, int shelfWidth) { int n = heights.size(); vector<int> dp(n + 1, 0); // dp[i] 表示前i本的最小总高度 dp[0] = 0; for (int i = 1; i <= n; ++i) { int max_height = 0; int total_width = 0; dp[i] = INT_MAX; for (int j = i - 1; j >= 0; --j) { total_width += widths[j]; if (total_width > shelfWidth) break; max_height = max(max_height, heights[j]); dp[i] = min(dp[i], dp[j] + max_height); } } return dp[n]; } int main() { // 示例输入 vector<int> heights = {3, 4, 2}; vector<int> widths = {6, 5, 8}; int shelfWidth = 10; int result = minTotalHeight(heights, widths, shelfWidth); cout << "Minimum total shelf height: " << result << endl; return 0; } ``` ### 实现说明 - `heights` 和 `widths` 分别表示每本的高度和宽度。 - `shelfWidth` 是书架的宽度限制。 - `dp[i]` 记录了前 `i` 本的最小总高度。 - 内层循环用于尝试不同的分段方式,计算当前层的最大高度,并更新 `dp[i]`。 ### 时间复杂度 - 该算法的时间复杂度为 $O(n^2)$,其中 $n$ 是籍的数量。 - 对于较小的数据规模(如 $n \leq 1000$),该算法可以高效运行。 ### 优化思路 - 如果籍数量较大,可以考虑使用单调队列或分治优化动态规划。 - 预处理 `max_height` 和 `total_width` 可以进一步优化性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值