pku3124 The Bookcase 扩展背包好题
题意:一堆书,每本书都有厚度和高度,管理员试图将这些书放到三层的书架上,每层都不能为空,求书架最小体积(书架深度显然为所有书深度最大值,所以与排放方案无关,故取1).
体积公式:
解题思想:
大概思路是DP,这个要先定下来
设v=(f(s1)+f(s2)+f(s3))*max{g(s1),g(s2),g(s3)}
其中g(s3)=SUM-g(s1)-g(s2)
f(s3)=max(h i)
就说说,我们有4个决策变量
一个变量留给DP决策,另一个变量利用DP状态设计来消除(这就需要对初始数据排序,保证决策过程中h i<=h p2,i属于s2,p2为当前决策点)。
这个问题就转化为在一个有序队列上选择两个分割点p1,p2,h p1<h p2,使得max{hi,i属于s1}<p1,max{hi,i属于s2}<p2,并且s1,s2,s3均不为空
状态可以设计为
F(i,t 1,t 2)
即考虑到第i个元素,s1的总厚度为t1,s2的总厚度为t2时p1所在元素的高度的最小值。
F(i,t1,t2)=min(F(i-1,t1,t2-data[i].t),data[i].h(当F(i-1,t1-data[i].t,t2)合法时),F(i-1,t1,t2)),显然,三个转移意义分别为将当前元素分配给s2、s1、s3
当F(i,t1,t2)由F(i-1,t1,t2-data[i].t)转移过来的时候试图更新全局最优解ans=min(ans,(data[i].h+F(i,t1,t2)+data[n-1].h)*t1*t2*(t total-t1-t2))。注意,这里说F(i,t1,t2)由F(i-1,t1,t2-data[i].t)转移过来指的是F(i-1,t1,t2-data[i].t)<=min(data[i].h(当F(i-1,t1-data[i].t,t2)合法时),F(i-1,t1,t2)),等于不能丢掉(原因?只有此时才能更新全局最优解)
代码如下
1 Source Code
2
3 Problem: 3124 User: yzhw
4 Memory: 17744K Time: 2188MS
5 Language: G++ Result: Accepted
6 Source Code
7 # include <cstdio>
8 # include <utility>
9 # include <functional>
10 # include <cstring>
11 # include <vector>
12 # include <algorithm>
13 using namespace std;
14 # define M 2105
15 # define N 100
16 int dp[M][M],n,total,ans,sum[N];
17 pair<int,int> data[N];
18 bool cmp(const pair<int,int> &a,const pair<int,int> &b)
19 {
20 return a.first<b.first;
21 }
22
23 int main()
24 {
25 //freopen("bookcase.in","r",stdin);
26 int test;
27 scanf("%d",&test);
28 for(int t=1;t<=test;t++)
29 {
30 scanf("%d",&n);
31 total=0;
32 ans=0xfffffff;
33 for(int i=1;i<=n;i++)
34 {
35 scanf("%d%d",&data[i].first,&data[i].second);
36 total+=data[i].second;
37 }
38 sort(data+1,data+n+1,cmp);
39 sum[0]=0;
40 for(int i=1;i<=n;i++)
41 sum[i]=sum[i-1]+data[i].second;
42 memset(dp,-1,sizeof(dp));
43 dp[0][0]=0;
44 for(int i=1;i<=n;i++)
45 {
46 for(int j=sum[i-1];j>=0;j--)
47 for(int k=sum[i-1];k>=0;k--)
48 if(dp[j][k]!=-1)
49 {
50 if(dp[j+data[i].second][k]==-1||dp[j+data[i].second][k]>data[i].first)
51 dp[j+data[i].second][k]=data[i].first;
52 if(dp[j][k+data[i].second]==-1||dp[j][k+data[i].second]>=dp[j][k])
53 {
54 dp[j][k+data[i].second]=dp[j][k];
55 if(j&&j+k+data[i].second!=total)
56 ans=min(ans,(dp[j][k]+data[i].first+data[n].first)*max(j,max(k+data[i].second,total-j-k-data[i].second)));
57 }
58 }
59 }
60 printf("%d\n",ans);
61
62 }
63 return 0;
64
65