动态规划专题(05):区间动态规划实践(乘法游戏)

2026.04.14

问题描述

题目描述(POJ1651):乘法游戏是用一些牌来玩的,在每张牌上都有一个正整数。玩家从一行牌中取出一张牌,得分的数量等于所取牌上的数字与左右两张牌上的数字的乘积。不允许取出第一张和最后一张牌。经过最后一步后,只剩下两张牌。玩牌的目标是把得分的总数降到最低。例如,若一行牌包含数字 10、1、50、20、5,则若玩家先拿出一张 1,然后拿出 20 和 50 的牌,得分便是 10×1×50 + 50×20×5 + 10×50×5 = 500 + 5000 + 2500 = 8000。若他按相反的顺序拿牌,即 50、20、1,则得分是 1×50×20 + 1×20×5 + 10×1×5 = 1000 + 100 + 50 = 1150。

输入:第 1 行包含牌的数量 n(3≤n≤100),第 2 行包含 1~100 的 n 个整数,表示牌上的数字。

输出:单行输出玩牌的最小分数。

        

问题分析

        依次取出中间的某一张牌,每次取牌得分为该牌数字与左右两张牌数字的乘积。最终剩下第一张和最后一张牌,目标是使总得分最小。

        设有 n 张牌,数字序列为 a[1..n]。每次操作是选择一个索引 k (1 < k < n),取出 a[k],得分为 a[k-1] × a[k] × a[k+1]。之后序列长度减1,原 a[k] 位置被移除,其左右元素相邻。

        关键理解要点

  1. 操作顺序影响结果:不同的取牌顺序会导致不同的总得分

  2. 最后剩下两张牌:即第一张和最后一张牌始终保留

  3. 区间独立性:当确定一个区间和最后取出的牌时,问题可以分解为子问题

暴力枚举(全排列)

        使用全排列方式对n张牌中的第2张到第n-1张进行序列号全排列,根据全排列后的序号按每种排列方式从前往后抽取对应的牌,抽取过程进行乘法计算与每种序列号内的结果相加。

        这里先构建一个n-2的全排列对应牌号的 1 ~ n-1,再根据全排列取出对应的牌号进行乘法计算,获取所有全排列中计算出来的最小值。

        代码示例:/dp_interval_01_multi_game_00_min_val_00.cc

// 题目描述(POJ1651):
// 乘法游戏是用一些牌来玩的,在每张牌上都有一个正整数。
// 玩家从一行牌中取出一张牌,得分的数量等于所取牌上的数字与左右两张牌上的数字的乘积。
// 不允许取出第一张和最后一张牌。经过最后一步后,只剩下两张牌。玩牌的目标是把得分的总数降到最低。
// 例如,若一行牌包含数字 10、1、50、20、5,则若玩家先拿出一张 1,然后拿出 20 和 50 的牌,
// 得分便是 10×1×50 + 50×20×5 + 10×50×5 = 500 + 5000 + 2500 = 8000。
// 若他按相反的顺序拿牌,即 50、20、1,则得分是 1×50×20 + 1×20×5 + 10×1×5 = 1000 + 100 + 50 = 1150。
// 输入:第 1 行包含牌的数量 n(3≤n≤100),第 2 行包含 1~100 的 n 个整数,表示牌上的数字。
// 输出:单行输出玩牌的最小分数。


// 暴力枚举法,全排列比较取最小值
// 这里先构建一个n-2的全排列对应牌号的 1 ~ n-1
// 再根据全排列取出对应的牌号进行乘法计算,获取所有全排列中计算出来的最小值
#include <iostream>
#include <stack>
#include <algorithm>
#include <vector>

using namespace std;

int MultiCalc(vector<int> & nums, const vector<int> & order)
{
    int res = 0;
    // flag用于标记对应位置的牌是否已经抽取,初始化false表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值