跟10154的差不多。
有两点不同。1是可承重不包括自重,2是编号小的不能放在大的上面。
果然看题解是不好的行为,做这道题的时候又被卡住了,其实刚做了10154没几天。
一直在最小承受重量这里犯糊涂。
dp[i][j] 表示使用前i个盒子组成一堆共有j个盒子的最小重量。
为什么要最小呢,只有最小才能保证可以堆成盒子的数目最多。这里是相通的。
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+p[i].a)
意思是当这堆盒子一共有j个的时候,不包含第i个盒子和包含第i个盒子,两种情况哪种更轻,并选择更轻的情况。
看上图。
行列皆由0开始,INF定义为正无穷大,表示未定义状态。
出于习惯我将【1 2】编号为1,【6 8】编号为2...【19 5 】编号为5
当j=1时,dp[1...5][1]表示从前五个盒子中选一个成为一堆的最小重量。
显然编号为1的【1 2】最合适。在dp[1][1]做出了选择编号1的盒子的决策,开一个数组last[i][j]表示dp[i][j]对应的一堆盒子最下面盒子的编号,那么这里last[1][1]=1。在dp[2..5][1]由于没有比只选1更好的决策了,所以都作出不选择的决策,也就是保持选择1的决策。
当j=2时,dp[1...5][2]表示从前五个盒子中选两个成为一堆的最小重量。
显然只有一个盒子是组不成有两个盒子的一堆的,所以为INF。
dp[2][2]时首先看编号2的盒子是否能放在之前的组成的一堆盒子下面。这里之前的状态指编号2的盒子没有参与组成,一堆盒子数目为j-1的盒子,也就是dp[1][1]。如果last[1][1]>2而且编号2的盒子可以承受dp[1][1]的重量,这时候编号2的盒子已经具备了放在dp[1][1]下面的条件,现在再看到底要不要选择编号2的盒子。取决于min(dp[i-1][j-1]+p[i].a,dp[i-1][j]),这个时候dp[i-1][j]也就是dp[1][2]是未定义状态,也就是说你只有把编号2的盒子放到dp[1][1]下面这唯一一种选择,因为你不放它,实际上dp[2][2]就只有1个盒子,必须再选择一个盒子才符合j=2的定义。
dp[2][3]时跟之前dp[2][2]相同,显然编号3的盒子也是具备放在下面的条件的。再看要不要选用编号3的盒子,看min(dp[i-1][j-1]+p[i].a,dp[i-1][j]),显然dp[1][2]+p[3].a=6要比dp[1][2]=7要小,所以选用编号3的盒子。翻译成中文是什么意思呢,就是说dp[2][1]放在编号3的盒子上面比dp[1][1]放在编号2的盒子上面要轻,所以dp[2][3]选择了更轻的情况。
在这里我们可以体会一下动规局部最优解组合成全局最优解的意思,dp[1][1],dp[2][1]以及dp[2][2]都是它们当时自身对应状态的最优解,现在到了dp[2][3],又由它们组合决策新的最优解。其实这些东西都是一层一层淘汰掉非优解,递推出来的,这样一步一步就可以得到最终结果。
j=3,4,5 类似。当然这里j最大取值是5,也就是盒子总数,实际上最终答案不一定是盒子总数。可能j=X某个时候,dp[i][X]是未定义状态,这时候根本就没有盒子可放了。
所以对于dp[N][j]搜索得到最大有定义状态的j就是答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define INF 0x7f7f7f7f
using namespace std;
struct Box
{
int a,A;
};
int dp[1005][1005];
int last[1005][1005];
Box p[1005];
int main()
{
int n;
while(scanf("%d",&n)&&n)
{
memset(dp,INF,sizeof(dp));
memset(last,-1,sizeof(last));
memset(p,-1,sizeof(p));
for(int i=n; i>0; --i) scanf("%d%d",&p[i].a,&p[i].A);
int ans=0;
for(int i=0; i<=n; ++i)
dp[i][0]=0;
for(int i=1; i<=n; ++i)
for(int j=1; j<=i; ++j)
{
if(p[i].A>=dp[i-1][j-1]&&i>last[i-1][j-1])
{
if(dp[i-1][j-1]+p[i].a<dp[i-1][j])
{
dp[i][j]=dp[i-1][j-1]+p[i].a;
last[i][j]=i;
}
else
{
dp[i][j]=dp[i-1][j];
last[i][j]=last[i-1][j];
}
}
else
{
dp[i][j]=dp[i-1][j];
last[i][j]=last[i-1][j];
}
if(dp[i][j]!=INF)
ans=max(ans,j);
}
printf("%d\n",ans);
}
return 0;
}
本文通过动态规划算法解决了一个关于箱子堆叠的问题。详细解释了如何通过最小化每一步的承载重量来最大化堆叠的箱子数量。利用二维数组dp记录从第一个到第i个箱子中选择j个箱子时的最小重量,同时使用last数组跟踪每一层选择的箱子编号,确保了箱子的正确堆叠顺序。最后,通过遍历dp数组找到最大定义状态的j作为最终答案。
280

被折叠的 条评论
为什么被折叠?



