【动态规划】最优时间表

第2题 最优时间表(sche.pas/cpp)

 

【问题描述】

一台精密仪器的工作时间为 n 个时间单位。与仪器工作时间同步进行若干仪器维修程序。一旦启动维修程序,仪器必须进入维修程序。如果只有一个维修程序启动,则必须进入该维修程序。如果在同一时刻有多个维修程序,可任选进入其中的一个维修程序。维修程序必须从头开始,不能从中间插入。一个维修程序从第s个时间单位开始,持续t个时间单位,则该维修程序在第s+t-1个时间单位结束。为了提高仪器使用率,希望安排尽可能少的维修时间。

   编程任务:对于给定的维修程序时间表,编程计算最优时间表。

 

【输入数据】

第 1 行有2 个正整数n和 k(1≤n,k≤9999)。n表示仪器的工作时间单位;k是维修程序数。接下来的k行中,每行有2 个表示维修程序的整数s和 t,该维修程序从第s个时间单位开始,持续t个时间单位。

 

【输出数据】

最少维修时间。

 

【样例输入】sche.in

15 6

1 2

1 6

4 11

8 5

8 1

11 5

 

【样例输出】sche.out

11


这个和尼克的任务是一样的。倒推可以避免不好处理,只有一个必须做。


#include <cstdio>
#include <string>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))

struct node
{
	long t;
	node* nxt;
};
node* head[20000];
long f[20000];

long getint()
{
	long rs=0;
	bool sgn=1;
	char tmp;
	do tmp=getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){tmp=getchar();sgn=0;}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

void insert(long a,long b)
{
	node* nn = new node;
	nn -> nxt = head[a];
	nn -> t = b;
	head[a] = nn;
}

int main()
{
	freopen("sche.in","r",stdin);
	freopen("sche.out","w",stdout);
	long n = getint();
	long k = getint();
	for (long i=1;i<k+1;i++)
	{
		long s = getint();
		long k = getint();

		if (s+k-1 > n)
			insert(s,n-s+1);
		else
			insert(s,k);
	}
	memset(f,0x3f,sizeof f);
	f[n+1] = 0;
	for (long i=n;i>0;i--)
	{
		if (!head[i])
		{
			f[i] = f[i+1];
		}
		for (node* vv=head[i];vv;vv=vv->nxt)
		{
			long t = vv->t;
			f[i] = min(f[i],f[i+t]+t);
		}
	}
	printf("%ld",f[1]);
	return 0;
}


<think>好的,我现在需要为用户讲解动态规划最优二叉搜索树问题。首先,我需要回忆一下动态规划的基本概念以及最优二叉搜索树的定义和应用场景。动态规划通常用于解决具有重叠子问题和最优子结构的问题,而最优二叉搜索树则是为了最小化搜索成本,这在数据库查询优化等领域有实际应用。 接下来,我应该明确问题的描述。用户可能想知道什么是最优二叉搜索树,如何用动态规划来解决它,以及具体的步骤和例子。我需要确保覆盖这些方面,并且用清晰的结构来组织内容。 首先,定义最优二叉搜索树:给定一组键和它们的概率(搜索成功和失败的概率),构造一棵二叉搜索树,使得平均搜索成本最小。这里需要区分成功搜索和失败搜索的概率,失败的情况通常对应于叶子节点下的“伪键”。 然后,动态规划的解决步骤。我需要解释如何将问题分解成子问题,建立状态转移方程,以及如何填充动态规划表。例如,使用二维数组dp[i][j]表示从键i到键j构建子树的最小期望成本,然后通过递推公式计算每个子问题的最优解。 在过程中,需要注意如何将成功概率和失败概率结合起来,计算每个子树的期望搜索成本。可能还需要提到权值的累加,以及如何选择根节点来分割左右子树,以最小化总成本。 此外,举一个具体的例子会有助于用户理解。例如,给定三个键和对应的概率,展示动态规划表的填充过程,以及最终如何构造出最优树的结构。 同时,我需要指出时间复杂度和空间复杂度,通常为O(n³)和O(n²),因为需要三层循环遍历所有可能的子树大小和根节点选择。可能还需要比较其他方法,比如贪心算法为什么不适用,以突出动态规划的必要性。 在思考过程中,我需要验证每一步是否正确,比如状态转移方程是否准确,是否考虑了所有可能的根节点位置,以及计算权值的方式是否正确。如果有不确定的地方,可能需要查阅相关资料或教材确认公式的正确性。 最后,总结动态规划解决最优二叉搜索树的关键点,强调其适用场景和算法优势,确保用户能够理解并应用该方法到实际问题中。</think>--- ### **动态规划最优二叉搜索树** #### **问题定义** 给定一组**有序键** $k_1 < k_2 < \dots < k_n$,每个键的**搜索成功概率**为 $p_i$,以及相邻键之间的**搜索失败概率**(伪键概率)为 $q_0, q_1, \dots, q_n$,构造一棵二叉搜索树,使得**总搜索期望成本最小**。 - **成功搜索成本**:键的深度 +1(根深度为0)。 - **失败搜索成本**:伪键的深度(在叶子节点的空指针处)。 --- ### **动态规划分析** #### **1. 最优子结构** 子树的最优解可以合并为全局最优解。 若根为 $k_r$,则左子树包含 $k_1, \dots, k_{r-1}$,右子树包含 $k_{r+1}, \dots, k_n$,左右子树也必须是最优的。 #### **2. 状态定义** 定义二维数组: - $dp[i][j]$:表示由键 $k_i$ 到 $k_j$ 构建的最优子树的**最小期望成本**。 - $w[i][j]$:表示键 $k_i$ 到 $k_j$ 及其伪键的**总概率和**,即: $$w[i][j] = \sum_{t=i}^{j} p_t + \sum_{t=i-1}^{j} q_t$$ #### **3. 状态转移方程** 对每个区间 $[i,j]$,枚举根节点 $r$($i \leq r \leq j$),计算以 $k_r$ 为根的最小成本: $$ dp[i][j] = \min_{i \leq r \leq j} \left( dp[i][r-1] + dp[r+1][j] + w[i][j] \right) $$ - **解释**: - $dp[i][r-1]$:左子树的期望成本。 - $dp[r+1][j]$:右子树的期望成本。 - $w[i][j]$:当前子树的总概率(作为根的深度贡献)。 #### **4. 边界条件** - 当 $j = i-1$ 时,子树为空,仅剩伪键 $q_{i-1}$,故 $dp[i][i-1] = q_{i-1}$。 --- ### **算法步骤** 1. **初始化**: - 对 $i=1$ 到 $n+1$,设置 $dp[i][i-1] = q_{i-1}$。 - 计算所有 $w[i][j]$(可通过递推优化:$w[i][j] = w[i][j-1] + p_j + q_j$)。 2. **递推填表**: - 按区间长度 $l = 0$ 到 $n-1$ 递增: - 对每个起点 $i=1$ 到 $n-l$,计算 $j = i + l$。 - 遍历所有可能的根 $r=i$ 到 $j$,更新 $dp[i][j]$。 3. **构造最优树**: - 记录每个区间 $[i,j]$ 的最优根节点 $root[i][j]$。 - 递归构建左子树($[i, r-1]$)和右子树($[r+1, j]$)。 --- ### **示例** 假设键 $k_1, k_2, k_3$ 的概率为 $p = [0.1, 0.2, 0.3]$,伪键概率 $q = [0.4, 0.1, 0.2, 0.3]$。 1. **计算 $w[i][j]$**: - $w[1][1] = p_1 + q_0 + q_1 = 0.1 + 0.4 + 0.1 = 0.6$ - $w[1][2] = w[1][1] + p_2 + q_2 = 0.6 + 0.2 + 0.2 = 1.0$ 2. **填表 $dp[i][j]$**: - $dp[1][1] = \min_{r=1} (dp[1][0] + dp[2][1] + w[1][1}) = q_0 + q_1 + 0.6 = 0.4 + 0.1 + 0.6 = 1.1$ 3. **最终结果**: - 最小期望成本为 $dp[1][3]$,最优根节点通过回溯确定。 --- ### **复杂度** - **时间复杂度**:$O(n^3)$(三重循环:区间长度、起点、根节点)。 - **空间复杂度**:$O(n^2)$(存储 $dp$ 和 $root$ 表)。 --- ### **应用场景** - 数据库索引优化:减少查询路径长度。 - 编译器符号表设计:加速符号查找。 --- ### **对比贪心法** - 贪心法无法保证全局最优(如直接选概率高的键为根),而动态规划通过枚举所有可能的根,确保最优性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值