题目链接:http://poj.org/problem?id=3124
题目大意:将n本书放入一个三层的书架,求书架的最小面积。
题目思路:这个题还真是难想啊,我连最优子结构都没有发现,这道题的做法先将书的高度降序排列,定义dp[i][j][k]为对于前i本书,第二个书架的宽度为j,第三个书架的宽度为k时书架的最小总高度,我们规定最高的书放在第一个书架。在开数组时可以省掉一维,则有转移方程:
if(j>b[i].w)
dp[j][k]=min(dp[j-b[i].w][k],dp[j][k]);
if(j==b[i].w)
dp[j][k]=min(dp[0][k]+b[i].h,dp[j][k]);
if(k>b[i].w)
dp[j][k]=min(dp[j][k-b[i].w],dp[j][k]);
if(k==b[i].w)
dp[j][k]=min(dp[j][0]+b[i].h,dp[j][k]);
但是单纯这样做会超时,下面引用一段话:
接着我们会发现还有一些冗余...:
对于任意一组数据,我们可以构造如下方案:
设最高书是高度是a,总宽度是b。那么a*3*(b/3+60)的答案是一定存在的,虽然往往不是最优。
就是说,三本最高的书分别放在三册,接着由于5 ≤ ti ≤ 30,所以总可以通过不断调整,使得三层宽度两两差<=60
。
接着看这句话:150 ≤ hi ≤ 300。你注意到了吗?肯定的,但你注意到前面的150≤hi了吗?
150 ≤ hi,hi ≤ 300说明什么?
两本最矮的书,和也不会小于最高的书!所以即便是最优方案,a*2的总高是至少的。
所以,当宽>(a*3*(b/3+60))/(a*2)=(b/3+60)*1.5的时候,它就一定不会成为最优解了。
这样,即便是极限数据,j,k<=1140。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<queue> #include<algorithm> #include<vector> #include<stack> #include<list> #include<iostream> #include<map> using namespace std; #define inf 0x3f3f3f3f #define Max 110 int max(int a,int b) { return a>b?a:b; } inline int min(int a,int b) { return a<b?a:b; } int t,n,sum; int dp[2200][2200]; struct node { int w,h; bool operator<(const node a)const { return h>a.h; } }b[100]; int main() { int i,j,k; scanf("%d",&t); while(t--) { scanf("%d",&n); sum=0; for(i=0;i<n;i++) { scanf("%d%d",&b[i].h,&b[i].w); sum+=b[i].w; } sort(b,b+n); for(i=0;i<=sum*0.5;i++) for(j=0;j<=sum*0.5;j++) dp[i][j]=inf; dp[0][0]=0; // printf("sum%d\n",sum); int ans=inf; for(i=1;i<n;i++) { for(j=sum*0.5;j>=0;j--) for(k=(sum-j<=sum*0.5?sum-j:sum*0.5);k>=0;k--) { if(j>b[i].w) dp[j][k]=min(dp[j-b[i].w][k],dp[j][k]); if(j==b[i].w) dp[j][k]=min(dp[0][k]+b[i].h,dp[j][k]); if(k>b[i].w) dp[j][k]=min(dp[j][k-b[i].w],dp[j][k]); if(k==b[i].w) dp[j][k]=min(dp[j][0]+b[i].h,dp[j][k]); // printf("i %d j %d k %d dp %d\n",i,j,k,dp[j][k]); } } for(j=0;j<=sum*0.5;j++) for(k=0;k<=sum*0.5;k++) { if(dp[j][k]!=inf&&(j&&k)) { if(sum>=j+k) { if((dp[j][k]+b[0].h)*(max(max(j,k),sum-j-k))<ans) { ans=(dp[j][k]+b[0].h)*(max(max(j,k),sum-j-k)); // printf("j %d k %d\n",j,k); } } } } printf("%d\n",ans); } }