单调栈解木板倒水问题

题目描述:

地上从左到右竖立着 n 块木板,从 1 到 n 依次编号,如下图所示。我们知道每块木板的高度,在第 n 块木板右侧竖立着一块高度无限大的木板,现对每块木板依次做如下的操作:对于第 i 块木板,我们从其右侧开始倒水,直到水的高度等于第 i 块木板的高度,倒入的水会淹没 ai 块木板(如果木板左右两侧水的高度大于等于木板高度即视为木板被淹没),求 n 次操作后,所有 ai 的和是多少。如图上所示,在第 4 块木板右侧倒水,可以淹没第 5 块和第 6 块一共 2 块木板,a4 = 2。

要使水不再往右延伸的话,那么必然要遇到一个比当前操作的木板更高的木板。

所以这道题转换成整型数组有n个元素,然后找到每个元素右边第一个大于该元素的数,并记录下标temp_id,那么第 i 块木板(下标为i)淹没木板的块数即为:

ai = temp_id - i - 1;    //从图示中即可计算出ai的值

这道题可以双重for循环直接暴力遍历,时间复杂度为O(n^2)。但结合单调栈的性质:使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。我们可以用单调栈求解。我个人有两种思路。第一种,从左往右将木板节点压栈,遇到比栈顶木板高的木板就将当前栈顶木板出栈并计算淹没的木板数,如此循环直到栈顶木板高度比当前木板高或者栈为空,然后将此木板压栈。木板全都压栈完成后,栈内剩余的木板都是右侧没有比它们更高的木板的,所以一个个出栈并计算ai=n+1-temp_id-1(用最右边无限高的木板减)。第二种思路稍微麻烦一些,即从右往左将木板压栈,这样的话栈内每个元素的后面一个元素必然是它右侧第一个比它大的元素。具体细节看代码:

 1 //从左往右解木板倒水
 2 int main() {
 3     int n,ans=0;
 4     cin>>n;
 5     Stack<Node> stack(n);
 6     Node temp;
 7     for(int i=1;i<=n;i++){
 8         cin>>temp.height;
 9         temp.id=i;
10         //遇到了右侧第一个比栈顶元素大的元素,计算并出栈
11         while(!stack.empty()&&stack.top().height<=temp.height){
12             ans=ans+i-stack.top().id-1;
13             stack.pop();
14         }
15         stack.push(temp);
16     }
17     //现在栈中的木板右侧没有比它高的木板,用最右侧无限高的木板减
18     while(!stack.empty()){
19         ans=ans+n+1-stack.top().id-1;
20         stack.pop();
21     }
22     cout<<ans<<endl;
23     return 0;
24 }
25 

26 //从右往左解木板倒水 27 int main(){ 28 int n,ans=0; 29 cin>>n; 30 Stack<Node> woods(n); 31 Stack<Node> stack(n); 32 Node temp; 33 for(int i=1;i<=n;i++){ 34 cin>>temp.height; 35 temp.id=i; 36 woods.push(temp); 37 } 38 //从右往左将木板压入栈 39 while(!woods.empty()){ 40 temp=woods.top(); 41 if(stack.empty()||temp.height<=stack.top().height){ 42 woods.pop(); 43 stack.push(temp); 44 } 45 else{ 46 Node t=stack.top(); 47 stack.pop(); 48 //为空表示节点t右侧没有比它高的木板,用无穷高木板减 49 if(stack.empty()){ 50 ans+=n+1-t.id-1; 51 } 52 //节点后面一个节点即为它右侧第一块比它高的木板 53 else{ 54 ans+=stack.top().id-t.id-1; 55 } 56 } 57 } 58 while(!stack.empty()){ 59 Node t=stack.top(); 60 stack.pop(); 61 if(stack.empty()){ 62 ans+=n+1-t.id-1; 63 } 64 else{ 65 ans+=stack.top().id-t.id-1; 66 } 67 } 68 cout<<ans<<endl; 69 return 0; 70 }

 

 

转载于:https://www.cnblogs.com/NoviScl/p/7001064.html

### 关于木板切割问题的遗传算法实现 木板切割问题是典型的优化问题之一,其目标是在给定尺寸的板材上合理安排多个形状各异的小部件,使得材料利用率最高并最小化废料。遗传算法因其强大的全局搜索能力,在处理此类复杂优化问题时表现出显著优势。 #### 遗传算法的核心概念 遗传算法是一种模拟自然选择过程的计算模型,主要通过编码、适应度函数评估、选择、交叉和变异等操作来寻找最优[^1]。对于木板切割问题,可以将其抽象为二维布局优化问题,其中每个个体表示一种可能的切割方案。 #### SVGnest 的应用 SVGnest 提供了一种基于遗传算法的决方案,用于决不规则装箱问题。该工具能够有效应对复杂的几何形状,并支持凹形边缘和零件内嵌情况。虽然 SVGnest 主要针对矢量图形文件(如 SVG),但它展示了如何利用遗传算法最大化材料利用率并减少浪费[^2]。 以下是使用 Python 编写的简单遗传算法框架,适用于木板切割问题: ```python import random from deap import base, creator, tools, algorithms # 参数设置 BOARD_WIDTH = 100 # 板材宽度 BOARD_HEIGHT = 50 # 板材高度 PARTS = [(20, 10), (30, 15), (40, 20)] # 小部件尺寸列表 # 创建适配器 creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) toolbox = base.Toolbox() # 初始化个体与种群 def create_individual(): individual = [] for part in PARTS: x = random.randint(0, BOARD_WIDTH - part[0]) y = random.randint(0, BOARD_HEIGHT - part[1]) rotation = random.choice([0, 90]) # 是否旋转 individual.append((x, y, rotation)) return creator.Individual(individual) toolbox.register("individual", tools.initIterate, creator.Individual, create_individual) toolbox.register("population", tools.initRepeat, list, toolbox.individual) # 定义适应度函数 def evaluate(individual): used_area = set() overlap_penalty = 0 waste_penalty = 0 for i, (x, y, r) in enumerate(individual): width, height = PARTS[i] if r == 90: # 如果旋转,则交换宽高 width, height = height, width for dx in range(width): for dy in range(height): px, py = x + dx, y + dy if not (0 <= px < BOARD_WIDTH and 0 <= py < BOARD_HEIGHT): # 超出边界 waste_penalty += 1 elif (px, py) in used_area: # 存在重叠 overlap_penalty += 1 else: used_area.add((px, py)) total_fitness = len(used_area) - overlap_penalty * 10 - waste_penalty * 5 return (total_fitness, ) toolbox.register("evaluate", evaluate) toolbox.register("select", tools.selTournament, tournsize=3) toolbox.register("mate", tools.cxTwoPoint) toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.05) # 运行遗传算法 pop = toolbox.population(n=50) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("avg", lambda vals: sum(vals)/len(vals)) stats.register("min", min) stats.register("max", max) result_pop, logbook = algorithms.eaSimple(pop, toolbox, cxpb=0.7, mutpb=0.2, ngen=50, stats=stats, halloffame=hof, verbose=True) print(f"最佳个体: {hof[0]}") ``` 此代码定义了一个简单的遗传算法框架,决了矩形小部件在固定大小板材上的排列问题。适应度函数综合考虑了面积利用率、重叠惩罚以及超出边界的浪费处罚。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值