PC/UVa:111103/10154
给出最多5607
只乌龟的重量和力量,求可以将乌龟摞多高。每只乌龟所承受的重量(自己的重量和上面所有乌龟的重量)不能超过它的力量。
通过这道题总结一下做动态规划题目的套路。
第一步,转换为分阶段决策(有的题目不需要明确的转换)。对于这道题,就是要将乌龟逐只加入,每加入一只做一次决策。但是乌龟摞起来后的最终顺序不一定和输入顺序相同,因此需要对输入的乌龟进行排序。
可以根据重量、力量和可承受重量(力量减重量)对乌龟进行排序,因为题目中要保证每只乌龟所承受的重量不能超过乌龟的力量,所以排序的指标应该是力量,或者可承受重量:
- 假设使用可承受重量进行递增排序。一只乌龟的可承受重量很小,可能是由于重量和力量都很大,导致差值很小,但是这个差值不能反应出乌龟的重量信息。这样排序可能把一个很重,但是力量也很大的乌龟放在上面。对下面的那些较轻,但是力量没有大到很重这个级别的乌龟来说,无论如果也不能撑起来上面的那只乌龟,但是将这两个乌龟交换一个位置也许是合理的
- 根据上面的分析,排序的指标应该保留重量和力量两方面的信息,也就是按照力量排序,力量相同再按照重量排序。对于相邻的两只乌龟来说,总重量是一样的,力量大一些的在下面,可以保证上面能放更多的乌龟,这个思路和4.6.5 鞋匠的烦恼有些类似,属于贪心排序?
第二步,寻找递推关系。这一步应该满足动态规划的最优子结构和无后效性。当乌龟数量较少时,可以通过穷举的方法,将各种合理的排列方式枚举出来,然后选择最高的一个。假设我们已经得到了所有可能的排列方式,现在要根据之前的排序结果,加入一只新的乌龟。这只乌龟可以放在已有排列方式的最上面,也可以放在最下面。如果放在最上面,那么下面每一只乌龟都需要检查力量是否合理,这样就无法将已有的结果作为整体对待,也就是不满足最优子结构性质;还有就是之前摞的顺序会对后面的决策有影响,不满足无后效性。但是如果放在最下面,那么只要这只乌龟的力量可以将自己以及已有的乌龟撑起来就OK,这样满足最优子结构性质,也满足无后效性。
第三步,合并重叠子问题。动态规划另一个性质就是存在重叠子问题,并且动态规划的本质是穷举。假如按照最长上升子序列(LIS)的方法来做,那么就是求当前乌龟在最下面时,能得到的最大高度,但是在这里是不对的。
在LIS问题规模较小时,也是枚举出各种可能的序列,然后将新的数加入到最后,组成新的序列。新的序列是否满足上升性质,只和已有序列的末尾和新的数的大小关系有关,所以只要已有序列的最后一个数相同,那么就是重叠的子问题,可以将它合并。
但是对于乌龟来说,虽然有多种已有排列使得最下面的乌龟是同一只乌龟,但是每种方式的重量可能不同,高度也可能不同,显然在高度相同时,应该保留整体重量最小的,而高度不同时则都应该保留。更一般的,应该不考虑最下面的乌龟具体是哪一只,在高度相同时保留整体重量最小的。
通过以上分析,需要记录的状态应该是i
只乌龟时,摞起来后得到不同高度h
(范围从0
到i
)时的最小整体重量,递推公式为W(i, h) = min(W(i - 1, h) + W(i - 1, h - 1) + turlte[i].weight)
,前面一项表示i - 1
只乌龟可以达到h
的高度,后面表示取前面i - 1
只乌龟高度为h - 1
的重量最小排列,再加上这只新的乌龟作为最底下的(当然需要满足力量的条件)。例如3
只乌龟时,可以得到的高度为0
、1
、2
、3
,在第四只乌龟放在高度为0
的下面时,可以得到一个新的高度为1
的整体重量,如果这个重量更轻就替换。
操作的本质是将新的乌龟放在每种保留的排列下面,得到一个新的高度增加1
的排列,如果这个高度比相同高度(上一轮得到的相同高度)的总重量小,则进行替换,所以可以去掉第一维的变量i
,只保留h
,公式为W(h) = min(W(h), W(h - 1) + turtle[i].weight)
。
递推新高度要从最大高度往最小高度循环,从公式中可以看出,如果h
从小到大循环,在使用h - 1
计算h
时,可能会覆盖旧的h
值对应的整体重量,这将对h + 1
的计算产生影响。
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
struct Turtle
{
int weight, measure;
int strength;
};
bool operator<(const Turtle &t1, const Turtle &t2)
{
if (t1.measure != t2.measure) return t1.measure < t2.measure;
else return t1.weight < t2.weight;
}
void stackTurtle(const vector<Turtle> &vecTurtle)
{
vector<vector<int>> Weight(vecTurtle.size() + 1, vector<int>(vecTurtle.size() + 1, INT_MAX));
for (size_t i = 0; i <= vecTurtle.size(); i++)
{
Weight[i][0] = 0;
}
Weight[1][1] = vecTurtle[0].weight;
for (size_t curr = 2; curr <= vecTurtle.size(); curr++)
{
const Turtle &turtle = vecTurtle[curr - 1];
Weight[curr] = Weight[curr - 1];
for (size_t h = curr; h > 0; h--)
{
if (Weight[curr - 1][h - 1] != INT_MAX){
int prevWeight = Weight[curr - 1][h - 1];
if (prevWeight + turtle.weight <= turtle.measure){
if (prevWeight + turtle.weight < Weight[curr][h]){
Weight[curr][h] = prevWeight + turtle.weight;
}
}
}
}
}
int maxHeight = 0;
for (int h = vecTurtle.size(); h > 1; h--)
{
if (Weight[vecTurtle.size()][h] != INT_MAX){
maxHeight = h;
break;
}
}
cout << maxHeight << endl;
}
void stackTurtle2(const vector<Turtle> &vecTurtle)
{
vector<int> Weight(vecTurtle.size() + 1, INT_MAX);
Weight[0] = 0;
Weight[1] = vecTurtle[0].weight;
for (size_t curr = 2; curr <= vecTurtle.size(); curr++)
{
const Turtle &turtle = vecTurtle[curr - 1];
for (size_t h = curr; h > 0; h--)
{
if (Weight[h - 1] != INT_MAX){//之前的乌龟可以摞到这个高度
int prevWeight = Weight[h - 1];
if (prevWeight + turtle.weight <= turtle.measure){//新的乌龟可以放在下面
if (prevWeight + turtle.weight < Weight[h]){//同样高度重量更优
Weight[h] = prevWeight + turtle.weight;
}
}
}
}
}
int maxHeight = 0;
for (int h = vecTurtle.size(); h >= 1; h--)
{
if (Weight[h] != INT_MAX){
maxHeight = h;
break;
}
}
cout << maxHeight << endl;
}
int main()
{
vector<Turtle> vecTurtle;
Turtle turtle;
while (cin >> turtle.weight >> turtle.measure){
turtle.strength = turtle.measure - turtle.weight;
vecTurtle.push_back(turtle);
}
sort(vecTurtle.begin(), vecTurtle.end());
stackTurtle2(vecTurtle);
return 0;
}
/*
300 1000
1000 1200
200 600
100 101
*/