动态规划 中山纪念中学 宋新波 本模板来源于网络,由第一课件网整理发布,免费分享给大家使用。

Slides:



Advertisements
Similar presentations
第2章 证券市场的运行与管理.
Advertisements

慢性扁桃体炎 本模板来源于网络,由第一课件网整理发布,免费分享给大家使用。
第 5 章 中國的都市.
第十五章 控制方法.
幼 兒 遊 戲 訪 談 組別:第七組 班級:幼保二甲 姓名:4A0I0008劉俐音 4A0I0043吳碧娟 4A0I0059劉又甄 4A0I0060江佳霓 4A0I0061蕭靖霓 4A0I0079王毓君.
快捷生活 ——王雨柠 f 王圣杰 f 王亦磊 f 李阳 f
第十章 原 子 核.
親愛的老師您好 感謝您選用本書作為授課教材,博碩文化準備本書精選簡報檔,特別摘錄重點提供給您授課專用。 說明: 博碩文化:
第七章 样本分布 数理统计是研究如何有效地收集、整理和分析带有随机影响的数据,从而对所观察的现象做出推断或预测,为决策提供依据的一门学科。
01 文化知识概述 1.1 如何理解文化 1)大家都很困惑 文化究竟是什么?似乎谁都知道,又似乎谁也说不清楚…… 对“文化”的定义多达两百多种,但没有公认的、令人满意的定义。 多数定义:广义的文化是人类创造的物质财富和精神财富的总和,狭义仅指精神财富。 “文化”一词,似乎能够包罗万象,又似乎很虚,虚到无法理解。
以蒙牛事业为己任 不以蒙牛利益为己有 ——蒙牛企业文化大纲.
引導者的角色 組別:第5組 4A1I0003 劉芷媛 4A1I0004 陳安琪 4A1I0014 陳佳瑩 4A1I0046 葉倢茹
第5章 回溯法 欢迎辞.
数列(一) 自强不息和谐发展 授课教师:喻永明.
“活力在基层”团日活动总结 佛山科学技术学院 13教育技术数媒2团支部 本模板来源于网络,由第一课件网整理发布,免费分享给大家使用。
組員:4A140013張瓊云 4A1I0039石宜芬 4A1I0909許峻綱 指導老師:王立杰老師
数据结构(C语言版) Data Structure
古文閱讀 – 像虎伏獸 明 劉基 組員: 5號江依倫 6號江若薇 12號張珉芫 32號蔡燕如.
图论算法 ---最大流问题 长沙市雅礼中学 朱 全 民.
青春足迹阅读- 男生贾里/哈利波特的分析 ——张桐需小组.
2009年6月 公共关系学电子教案 第十二讲 组织形象的塑造 谢振安.
寻常物 内涵深 ——“借 物 喻 人”写作方法指导 科目:语文科 授课老师:陈亮清 单位:越秀区惠福西路小学.
資料結構與演算法 課程教學投影片.
第5章 回溯法 “通用的解题法” 欢迎辞.
指導老師:陳韻如 班級:幼保二甲 姓名:林靜宜 學號:4A0I0033
普及组近5年NOIP试题分析 安徽师大附中 叶国平.
第8章 回归分析 本章教学目标: 了解回归分析在经济与管理中的广泛应用; 掌握回归分析的基本概念、基本原理及其分析应用的基本步骤;
第六章 计算智能 6.1 概述 6.2 神经计算 6.3 进化计算 6.4 模糊计算 6.5 粗糙集理论 6.6 其他.
課程名稱:資料結構 授課老師:_____________
第十章 原子核 核外电子-原子物理学 原子核-原子核物理学.
第八章 第一节 日本 邹旭丹 滨河中学初中部 湘教版地理初一年级.
Lecture 5 Dynamic Programming
递推算法 递推是一种重要的数学方法,在数学和计算机领域都有广泛应用。这种算法的特点是:一个问题的求解需要一系列的计算,在已知条件和所求问题之间总存在某种相互联系的关系。在计算时,可以找到前后过程间的数量关系,即递推。递推算法包括顺推和逆推。 递推算法的关键在于找到相邻数据项之间的关系,即递推关系,建立递推关系式。我们有时将递推算法看成一种特殊的迭代算法。
Chapter 13 數論基礎.
华电开关VI系统介绍及应用 李珊. 华电开关VI系统介绍及应用 李珊 目 录 1 CIS企业形象识别系统介绍 2 VIS视觉识别系统介绍 3 华电开关VI系统应用 4 华电开关品牌文化传播案例.
8章 习题 1. 设有表达式A*(B*C-A)≤B+C∧D a.试写出逆波兰式中间代码。 b.试写出三元式中间代码。 c.试写出树中间代码。
编译原理实践 5.给定语法的语法分析程序构造.
通 信 原 理 指导教师:杨建国 指导教师:杨建国 二零零七年十一月 二零零八年三月.
第五章 语法分析——自下而上分析 自上而下分析法(Top-down) 自下而上分析法(Bottom-up) 国防科技大学计算机系602教研室.
第二章 插值.
第6章 计算机的运算方法 6.1 无符号数和有符号数 6.2 数的定点表示和浮点表示 6.3 定点运算 6.4 浮点四则运算
主讲人: 吕敏 { } Spring 2016 ,USTC 算法基础 主讲人: 吕敏 { } Spring 2016 ,USTC.
For x = 0 To 9 For y = 0 To 9 z = *x + 10*y …… Next y
第四章 第三节 最短路径算法.
第二章 基本遗传算法(GA) 2.1 基本遗传算法描述 基本遗传算法的构成要素 (1) 染色体编码方法
计算机算法基础 周中成 苏州科技学院应用数学系 2019/4/18.
山东师范大学信息科学与工程学院软件工程研究所 徐连诚 2006年11月20日
第5章 回溯法 欢迎辞.
第九章 數論基礎.
1.3 算法案例 第一课时.
数据结构选讲 北京大学 陈瑜希.
第4章 贪心方法.
學這些有什麼好處呢? 為了把資料作更客觀之總結描述或比較多組資料。總而言之,就是要找出一個數能代表整組數據。
第1章 绪论(二) 教学目标 理解算法的特性及评价标准 掌握算法时间复杂度和空间复杂度的分析方法 1/
对质点动力学问题: 建立质点运动微分方程求解。
课前注意 课前注意 大家好!欢迎加入0118班! 请注意以下几点: 1.服务:卡顿、听不清声音、看不见ppt—管家( ) 2.课堂秩序:公共课堂,勿谈与课堂无关或消极的话题。 3.答疑:上课听讲,课后答疑,微信留言。 4.联系方式:提示老师手机/微信: QQ:
算法导论第一次习题课.
第3章 多维随机向量及其分布 3.1 随机向量及其联合分布函数 3.2 二维离散型随机向量 3.3 二维连续型随机向量
第三章 DFT 离散傅里叶变换.
第3章 空间力系的简化与平衡 §3–1 空间力系的简化 §3–2 空间力系的平衡 §3–3 物体的重心 §3–4 平行力系中心.
問題解決與流程圖 高慧君 台北市立南港高中 2006年12月22日.
动态规划(五).
第二章 基本遗传算法(GA) 2.1 基本遗传算法描述 基本遗传算法的构成要素 (1) 染色体编码方法
第六章 贪心算法.
随机算法 东南大学计算机学院 方效林.
第八章 服務部門成本分攤.
动态规划(七).
PASCAL语言 吉林大学计算机科学与技术学院.
算法基础习题课2 助教:刘倩玉.
德育网新闻发布规范 培训单位:暨大研究生会组织编辑部 本模板来源于网络,由第一课件网整理发布,免费分享给大家使用。
Presentation transcript:

动态规划 中山纪念中学 宋新波 本模板来源于网络,由第一课件网整理发布,免费分享给大家使用。 更多精彩PPT模板,请访问http://www.1kejian.com 使用时删除此备注即可。 配色方案修改: 配色方案在【格式】-->【幻灯片设计】-->【配色方案】-->【编辑配色方案】下调整。 LOGO的添加: Logo添加修改在【视图】-->【母版】-->【幻灯片母版】下调整。直接选择logo图片删除或修改。 字体格式的设置: 括标题和文本格式的设置在【视图】-->【母版】-->【幻灯片母版】下调整。

动态规划的关键点 最优化原理 子问题最优化结构 无后效性 未来与过去无关 状态 描述最优解的结构 状态转移方程 递归定义最优解的值 程序实现 用记忆化搜索或迭代法求解

例1:01背包 有N种物品和一个容量为V的背包。第i种物品只有1个,体积是v[i],价值是w[i]。选择物品装入背包使这些物品的体积总和不超过背包容量,且价值总和最大,求出这个最大价值。

分析 状态:f[i,j]表示用体积为j的背包装前i个物品能获得的最大价值。 考虑第i种物品装或不装进行状态转移: 1.装:f[i-1,j-v[i]]+w[i](必须满足j>=v[i]) 2.不装:f[i-1,j] 两种情况取较大值。 状态转移方程为: 0 i=0(边界条件) f[i,j]= f[i-1,j] j<v[i] max(f[i-1,j],f[i-1,j-v[i]]+w[i]) j>=v[i] 答案为f[n,v],时间复杂度为O(N*V)。

例2:完全背包 有N种物品和一个容量为V的背包。第i种物品有无穷个,体积是v[i],价值是w[i]。选择物品装入背包使这些物品的体积总和不超过背包容量,且价值总和最大,求出这个最大价值。

方法一 状态:f[i,j]表示用体积为j的背包装前i个物品能获得的最大价值。 考虑第i个物品装几个来进行状态转移,假设装x个,x的范围为0<=x<=j div v[i] 状态转移方程: 0 i=0 f[i,j]= max{f[i-1,j-x*v[i]]+x*w[i]} 0<=x<=j div v[i] 答案为f[n,v],时间复杂度为o( )

方法二 状态:f[i,j]表示用体积为j的背包装前i个物品能获得的最大价值。 考虑第i个物品装或不装来进行状态转移: 1.装:必须满足j>=v[i],由于物品有无穷多个,装一次后后面还可以再装,所以状态为f[i,j-v[i]]+w[i]; 2.不装:f[i-1,j] 状态转移方程: 0 i=0 f[i,j]= f[i-1,j] j<v[i] max(f[i-1,j],f[i,j-v[i]]+w[i]) j>=v[i] 答案为f[n,v],时间复杂度为O(N*V)。

例3:多重背包 有N种物品和一个容量为V的背包。第i种物品最多有m[i]件可用,体积是v[i],价值是w[i]。选择物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

方法一 状态:f[i,j]表示用体积为j的背包装前i个物品能获得的最大价值。 跟完全背包方法一相同,考虑第i个物品装几个来进行状态转移,假设装x个,x的范围为0<=x<=min(j div v[i],m[i]) 状态转移方程: 0 i=0 f[i,j]= max{f[i-1,j-x*v[i]]+x*w[i]} 其中0<=x<=min(m[i],j div v[i]) 答案为f[n,v],时间复杂度为O(V* )。

方法二 把“m[i]个第i种物品”看作是“m[i]种物品”,每种物品只有一个,体积和价值分别为v[i]和w[i],这样原问题就转变为 个物品的01背包问题。 时间复杂度也是O(V* )。

方法三 应用二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略(取0..m[i]件)均能等价于取若干件代换以后的物品。另外,取超过m[i]件的策略必不能出现。 方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的体积和价值均是原来的体积和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),m[i]-2^k+1,其中k满足m[i]-2^k+1<2^k。例如,如果m[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。 如何证明任何x(0<=x<=m[i])都可以用上述系数相加得到?

方法三 证明: 当x=0时:只要都不选就可以; 当1<=x<=2^k-1时:把x转为二进制,选择对应位为1的物品。如x=5,5的二进制为101,5=1+4; 当2^k<=x<=m[i]时:由于m[i]-(2^k-1)<2^k,则选择m[i]-(2^k-1),剩下的为x-m[i]+2^k-1<=2^k-1可以由前面的1,2,...,2^(k-1)组合得到。 物品i可以被分解为 个数量为1的物品,题目就转化为 个物品的01背包。 时间复杂度为O(V* )。

方法四 回顾方法一状态转移方程: 0 i=0 f[i,j]= max{f[i-1,j-x*v[i]]+x*w[i]} 0<=x<=min(m[i],j div v[i]) 可以用单调队列优化到O(N*V)。

例4:最长上升子序列 给你n个数a1,a2,...,an的序列,要求输出最长上升子序列的长度。

方法一 状态:f[i]表示以a[i]结尾的最长上升子序列的长度。 考虑前一个数可能的位置进行状态转移,假设前一个数是a[j],则必须满足j<i同时a[j]<a[i],满足条件的j有多个,找出最大的f[j]即可。 状态转移方程为: 1 i=1 f[i]= max{f[j],0}+1 (j<i)and(a[j]<a[i]) 答案为max{f[i]}(1<=i<=n)。时间复杂度为O(n2)。

方法二 方法一中计算f[i]时需要从1到i-1逐一枚举j,我们可以记录g[x]表示,以数值x结尾的最长上升子序列的长度,利用树状数组维护g[1..x]的最大值,在计算f[i]时直接利用树状数组计算出g[1..a[i]-1]的最大值。 如果a[i]数值不大可以直接做,否则需要离散化。 时间复杂度为O(nlgn)。

方法三 贪心法。 上升子序列的最后一个数越小越好。 记录b[i]表示目前已经计算出来的长度为i的上升子序列的最后一个元素的最小值。数组b是有序的。 依次处理a1,a2..,a[n],对于a[i],二分求出在b[]数组中插入的位置p,f[i]=p,同时把b[p]改为a[i] 时间复杂度为O(nlgn)。 b[4] 6 i 1 2 3 4 5 6 a[i] f[i] b[3] 5 b[2] 2 4 2 1 3 2 4 b[1] 1 3

例5:石子归并一 设有n堆石子排成一排,其编号为1,2,3,…,n(n≤100)。每堆石子有一定的数量,如下表13 7 8 16 21 4 18。现在要将n堆石子归并成一堆。每次只能将相邻的两堆石子堆成一堆,这样经过n-1次归并之后最后成为一堆,如上面7堆沙子,可以有多种方法归并成一堆,其中的2种方法如下图:

例5:石子归并一 如上图中,将13和7归并为一堆的代价为20。归并总代价指的是将沙子全部归并为一堆沙子的代价的和,如上面的2种归并方法中, (a)的总代价为20+24+25+44+69+87=267 (b)的总代价为15+37+22+28+59+87=248 由此可见,不同归并过程得到的总的归并代价是不一样的。 问题:当n堆沙子的数量给出之后,找出一种合理的归并方法,使总的归并代价最小。

分析 最后那堆石子是由一开始的n堆石子经过n-1次合并得到,考虑最后一次合并,一定是把两堆石子合并成一堆,那最后一次合并的两堆石子分别是怎么来的,它们分别对应于最初的n堆石子中的哪些石子? 如图所示,由于每次选择相邻两堆石子合并,所以一定是由第1堆到第i堆合并成其中一堆,另一堆是由第i+1堆到第n堆合并而成。而左上这堆石子又是由1到j合并的石子堆跟j+1到i合并的石子堆这两堆石子合并而成。 1到j合并而成 j+1到i合并而成 i+1到n合并而成 1到i合并而成 1到n合并而成

分析 根据上述分析,我们定义f[i,j]表示把第i堆到第j堆石子合并成一堆所需的最小代价。s[i]表示前i堆石子和。 最后一次合并左边一堆由i到k合并而成,右边一堆由k+1到j合并而成,在所有可选择的k中找一个最优的,得到以下状态转移方程: 0 i=j f[i,j]= max{f[i,k]+f[k+1,j]}+s[j]-s[i-1] (i<=k<=j-1) 按照石子堆数划分阶段来实现。 答案为f[1,n],时间复杂度为O(n3)。

程序 for i:=1 to n do s[i]:=s[i-1]+a[i]; for L:=2 to n do begin{L表示石子堆数} for i:=1 to n-L+1 do begin{i表示起始位置} j:=i+L-1;{j表示终点位置} f[i,j]:=maxlongint; for k:=i to j-1 do begin f[i,j]:=min(f[i,j],f[i,k]+f[k+1,j]+s[j]-s[i-1]); end; writeln(f[1,n]);

例6:石子归并二 设有n堆石子围成一圈,其编号为1,2,3,…,n(n≤100)。每堆石子有一定的数量,如下表13 7 8 16 21 4 18。现在要将n堆石子归并成一堆。每次只能将相邻的两堆石子堆成一堆,这样经过n-1次归并之后最后成为一堆,当n堆沙子的数量给出之后,找出一种合理的归并方法,使总的归并代价最小。

方法一 可以把圈看作是n条线,然后再按照“排成一排”的做法来做。 时间复杂度为O(n4),超时。

方法二 方法一中把圈看作n条线,这n条线之间有重叠,但方法一把这n条线独立开来做导致有些状态重复计算,所以超时。 f[i,j]表示从第i堆到第j堆合并成一堆所需最少代价,如果j>i则一共有j-i+1堆石子,分别是i,i+1,...,j-1,j,如果j<i,则一共有n-i+1+j堆石子,分别是i,i+1,...,n,1,2,...,j 状态转移方程: 0 i=j f[i,j]= max{f[i,k]+f[k mod n+1,j]}+s1(i,j) 当i<j时s1[i,j]=s[j]-s[i-1],i<=k<=j-1; 当i>j时,s1(i,j)=s[n]-s[i-1]+s[j],k取i,i+1,...n,1,2,...j-1)。 答案为max{f[1,2],f[2,1],f[3,2],f[n,n-1]},时间复杂度为O(n3)。

方法二程序 for L:=2 to n do begin{L表示石子堆数} for i:=1 to n do begin {i为起点,注意i的终值为n} j:=(i+L-2)mod n+1;{j为终点} k:=i;f[i,j]:=maxlongint; while k<>j do begin f[i,j]:=min(f[i,j],f[i,k]+f[k mod n+1,j]+s1(i,j)); k:=k mod n+1; end;

方法三 把n个石子堆复制一份变成a1,a2,...,an,a1,a2,...an,状态f[i,j]和转移方程都跟前面类似。这样n条线的最小代价分别对应于f[1,n],f[2,n+1],f[3,n+2]...f[n,2*n-1],其中最小值就是答案。 时间复杂度为O(n3),程序如下: for i:=n+1 to 2*n do a[i]:=a[i-n]; for L:=2 to n do begin{L表示石子堆数} for i:=1 to 2*n-L do begin{i表示起点} j:=i+L-1;{j表示终点} f[i,j]:=maxlongint; for k:=i to j-1 do f[i,j]:=min(f[i,j],f[i,k]+f[k+1,j]+s[j]-s[i-1]); end; end;ans:=f[1,n]; for i:=2 to n do ans:=min(ans,f[i,i+n-1]);

例7:加分二叉树(NOIP2003) 设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: (subtree的左子树的加分)×(subtree的右子树的加分)+subtree的根的分数 若某个子树为空,规定其加分为1,叶子的加分就是叶节点本 身的分数。不考虑它的空子树。 试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出; (1)tree的最高加分 (2)tree的前序遍历

例7:加分二叉树(NOIP2003) 【输入格式】 第1行:一个整数n(n<30),为节点个数。 第2行:n个用空格隔开的整数,为每个节点的分数(分数<100) 【输出格式】 第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。 第2行:n个用空格隔开的整数,为该树的前序遍历。 【输入样例】 【输出样例】 5 145 5 7 1 2 10 3 1 2 4 5

分析 树的中序遍历=左子树的中序遍历+树根+右子树的中序遍历。 已知中序遍历为1到n,如果树根为i,则1-i-1为这棵树的左子树的中序遍历,而i+1到n为右子树的中序遍历,每一棵子树都对应着序列中一段连续的区域。 定义opt[i,j]表示中序遍历为i到j的树最大加分,如果树根为k,则左子树为i到k-1,右子树为k+1到j,左子树和右子树对应的最大加分为opt[i,k-1]和opt[k+1,j],满足最优化原理。

分析 通过考虑树根k进行状态转移: a[i] i=j(叶子) opt[i,j]= 1 i=j+1(空子树) max{opt[i,k-1]*opt[k+1,j]+a[k]} i<=k<=j 答案为opt[1,n],时间复杂度为o(n3)。 对于输出最大加分二叉树的前序,只需增加g[i,j]记录opt[i,j]取最大值所对应的k即树根,再用递归输出前序即可。

程序代码——计算opt for i:=1 to n do f[i,i-1]:=1; for i:=1 to n do opt[i,i]:=a[i]; for L:=2 to n do {L表示序列的长度} for i:=1 to n-L+1 do begin {i表示序列的起点} j:=i+L-1; opt[i,j]:=-maxlongint; for k:=i to j do begin t:=opt[i,k-1]*opt[k+1,j]+a[k]; if t>opt[i,j] then begin opt[i,j]:=t;g[i,j]:=k; end;

程序代码——输出前序 procedure print(L,R:integer); begin if L>R then exit; if L=R then write(L,' ') else begin write(g[L,R],' '); print(L,g[L,R]-1); print(g[L,R]+1,R); end;

例8:能量项链(NOIP2006) 【问题描述】 在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。 需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。 例如:设N=4,4颗珠子的头标记与尾标记依次为(2,3) (3,5) (5,10) (10,2)。我们用记号⊕表示两颗珠子的聚合操作,(j⊕k)表示第j,k两颗珠子聚合后所释放的能量。则第4、1两颗珠子聚合后释放的能量为: (4⊕1)=10*2*3=60。 这一串项链可以得到最优值的一个聚合顺序所释放的总能量为 ((4⊕1)⊕2)⊕3)=10*2*3+10*3*5+10*5*10=710。

例8:能量项链 【输入文件】 输入文件energy.in的第一行是一个正整数N(4≤N≤100),表示项链上珠子的个数。第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1≤i≤N),当i<N时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。 至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。 【输出文件】 输出文件energy.out只有一行,是一个正整数E(E≤2.1*10^9),为一个最优聚合顺序所释放的总能量。 【输入样例】 4 2 3 5 10 【输出样例】 710

分析 用a[i]表示珠子i的头标记,则把第i个到第j个珠子聚合成一个珠子的头标记为a[i],尾标记为a[j mod n+1]。 做法跟环形石子归并一样,把n个珠子复制一份,用f[i,j]表示把第i个到第j个珠子聚合成一个珠子的最大能量。 状态转移方程如下: 0 i=j f[i,j]= max{f[i,k]+f[k+1,j]+a[i]*a[k+1]*a[j+1]} i<=k<=j-1 答案=max{f[1,n],f[2,n+1],....,f[n,2*n-1]}。 时间复杂度为O(n^3)。

能量项链程序 for i:=n+1 to 2*n do a[i]:=a[i-n]; for L:=2 to n do for i:=1 to 2*n-L do begin j:=i+L-1; f[i,j]:=0; for k:=i to j-1 do f[i,j]:=max(f[i,j],f[i,k]+f[k+1,j]+a[i]*a[k+1]*a[j+1]); end; ans:=0; for i:=1 to n do ans:=max(ans,f[i,i+n-1]); writeln(ans);

例9:凸多边形三角划分 给定一个具有N(3<=N<=50)个顶点(从1到N编号)的凸多边形,每个顶点的权均已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小? 输入文件:第一行 顶点数N 第二行 N个顶点(从1到N)的权值 输出格式:最小的和的值 输入示例: 4 1 2 3 4 输出示例:18

分析 在递推中已经学习过“凸多边形划分”一题,思路一样,每一种划分都保证多边形的每条边都唯一属于一个三角形,只要把这条边对应的点定下来,这个三角形就是唯一确定的。 状态f[i,j](i<j)表示i到j这j-i+1个顶点形成的凸多边形乘积最小值。注意这个凸多边形共有j-i+1条边,分别是(i,i+1),(i+1,i+2)...(j-1,j)以及(i,j)这条边。 考虑(i,j)这条边所对应的顶点k进行状态转移,i+1<=k<=j-1,此时多边形被分成三个区域,其中区域是i到k形成的凸多边形,区域是△ijk,区域为k到j形成的凸多边形。 0 j=i+1 f[i,j]= min{f[i,k]+f[k,j]+a[i]*a[j]*a[k]} i+1<=k<=j-1 时间复杂度为O(n3)。答案为f[1,n]。 k j i 1 2 3

例10:行政划分 【背景】某国领土形状十分奇特,可以将它近似地看作是一个有N个顶点的凸多边形 。现在该国政府想要将它划分为(N-2)个互不重叠的行政区域,并希望每个区域的形状都是三角形,三角形的顶点即为凸多边形的顶点。该国政府又出于资源、人口、宗教等多方面的考虑,希望得到一种划分方案,使得(N-2)块区域的面积的标准差最小 【任务】现在该国政府官员找到了你,希望你能够帮助他们解决这个问题, 【输入格式】第一行是一个整数N (4≤N≤50), 表示凸多边形顶点的个数 接下来的N 行,每行有两个实数X、Y(-1000000≤X、Y≤1000000),分别表示按比例缩小后的凸多边形顶点在坐标系内的横纵坐标值。 【输出格式】你只要输出一个实数,即最小标准差值 (保留到百分位)。 【样例输入】 【样例输出】 4 0.00 0 0 1 0 1 1 0 1

分析 标准差= ,只要保证 标准差一定最小。 所以本题就转化为求一种划分方案使得n-2个三角形的面积平方和最小。方法同例9,同学们自己完成。

例11:等腰三角形 给定一个正N边形,可以通过连线将这个多边形分割成N-2个三角形,问这N-2个三角形中恰有k个等腰三角形的分割方法有多少?这个值可能很大,输出对9397取模的结果。 输入格式:第一行一个数n和k。(3<=n<=50,0<=k<=n-2). 输出格式:一个整数,表示正n边形切割成n-2个三角形,其中恰有k个等腰三角形的分割方法,结果模9397。 样例输入: 样例输出: 4 2 2 3 0 0 5 3 5 注释:于第一种情况下,我们之间可以有一个顶点0和2或1和3之间的对角顶点。在这两种情况下,有2个等腰的三角形。对于第二个案件中,只有一个等边三角形的分割不含对角线,只有一等腰三角形(多边形本身)。 对于第三种情况,一个正五边形有5个三角剖分。每个顶点都与它们不相邻的两点连接。

方法一 状态f[i,j,k]表示以i到j这j-i+1个点为顶点形成的凸多边形划分成j-i-1个互不相交的三角形,其中有k个等腰三角形的方案数。 考虑(i,j)这条边对应的顶点x,另还要考虑i到x的凸多边形中有多少个等腰三角形进行状态转移。 如何判断(i,j,x)是否是等腰三角形? 由于是正N边形,所以三条边的长度可以看作是x-i,j-x,j-i。定义judge(i,j,k)判断i,j,k三点形成的三角形是否是等腰三角形。如果是则返回1否则返回0。 状态转移方程如下: 1 (j=i+1)and(k=0) f[i,j,k]= j>i+1 答案为f[1,n,k]。时间复杂度为O(N3*K2)。

方法二 可以在方法一的基础上优化状态,方法一中用两个参数i,j来表示一个凸多边形,由于是正N边形,可以用一个参数记录点数即可。 g[i,j]表示把以i个连续点为顶点的凸多边形划分成i-2个三角形其中有j个等腰三角形的方案数。 状态转移方程为: 1 (i=2)and(j=0) g[i,j]= i>=3 答案为g[n,k]。时间复杂度为O(N2*K2)。

例12:最优配对问题 平面上有n个点P1,P2,...,Pn,你的任务是把它们配成n/2对(n是偶数),使得每个点恰好在一个点对中。所有点对中两点的距离之和应尽量小。n<=20,|xi|,|yi|<=10000。

方法一 n<=20,容易想到用搜索来解决,每次从未配对的点中选两个点配对再继续搜。定义全局数组f:array[0..20]of boolean;f[i]=false表示i未配对,f[i]=true表示i已经配对,dis(i,j)表示Pi到Pj的距离,答案ans一开始赋为100000000。

方法一程序 procedure dfs(remain:integer;d:real);{remain表示剩下的点数,d表示当前的距离} var i,j:integer; Begin if d>=min then exit;{最优化剪枝} if remain=0 then ans:=d {到达递归出口} else for i:=1 to n-1 do if not f[i] then {枚举i和j配对} for j:=i+1 to n do if not f[j] then begin f[i]:=true;f[j]:=true; dfs(remain-2,d+dis(i,j)); f[i]:=false;f[j]:=false; end; 时间复杂度为 ,超时。

方法二 方法一有很多方案被重复计算了,如n=4,(1,3)(2,4)和(2,4)(1,3)是同一种方案,但被计算了两次。 由于每个点都要配对,我们可以按照P1,P2,...Pn的顺序来依次寻找跟它们配对的点

方法二程序 procedure dfs(x:integer;d:real);{x表示下一个要配对的点} var y:integer; Begin if d>=min then exit;{最优化剪枝} if x=n+1 then ans:=min(ans,d) else if f[x] then dfs(x+1,d) else for y:=x+1 to n do if not f[y] then begin f[y]:=true;dfs(x+1,d+dis(x,y));f[y]:=false; end; 时间复杂度为n!/(2^(n/2)*(n/2)!) ,比方法一快了很多,但还是超时。

方法三 方法二中依然存在状态重复计算。如n=8,目前已经搜到的配对有2个,分别是(1,3)(2,4),还有5678未配对,另一种搜到了(1,4)(2,3),剩余未配对的也是5678,那么寻找5678配对方案就被重复搜索了。 要解决这个问题,需要用记忆化搜索,此时本质上已经是动态规划了,要用记忆化搜索,必须首先确定好状态的参数,这里的参数实际就是一些未配对的点的集合,集合不方便存储,采用二进制压缩存储,每个点对应一个二进制位,未配对的记为1,已经配对的记为0。如n=8,未配对的点为1,3,5,7,则对应的二进制为01010101,对应的十进制为85,则把(1,3,5,7)配对的最小值存储在f[85]中。 f[i]表示把配对情况二进制值为i中的未配对点进行配对获得最小距离。通过考虑下一个配对点进行状态转移: f[i]=min(f[i xor(1 shl (x-1))xor(1 shl(y-1))]+dis(x,y)) (i and(1 shl(x-1))<>0)and(i and(1 shl(y-1))<>0)

方法三程序 for i:=1 to 1 shl n-1 do begin f[i]:=100000000; for x:=1 to n-1 do begin if i and (1 shl(x-1))<>0 then begin for y:=x+1 to n do begin if i and(1 shl(y-1)) then f[i]:=min(f[i],f[i xor(1 shl(x-1))xor(1 shl (y-1))]+dis(x,y)); end; 时间复杂度为O(2n*n2),仍然超时。

方法四 方法三中有重复计算,x不用枚举每一种情况,直接在未配对点中选择最小的点作为x。 for i:=1 to 1 shl n-1 do begin f[i]:=100000000; for x:=1 to n-1 do if i and (1 shl(x-1))<>0 then break; for y:=x+1 to n do begin if i and(1 shl(y-1))<>0 then f[i]:=min(f[i],f[i xor(1 shl(x-1))xor(1 shl (y-1))]+dis(x,y)); end; 时间复杂度为O(n*2n)。 证明查找第一个未配对的点x平均判断次数不超过2。

证明 用i表示第一次出现1的位置,1<=i<=n,第一次出现1的位置为i的数有2n-i。设T表示总判断次数。则有: 平均值=T/(2n)<2

例13:Islands and Bridges(poj2288) 给你一幅地图,地图上有岛屿和桥梁,桥梁连接着岛屿,众所周知,汉密尔顿路是指通过桥梁经过每个岛屿一次而且只有一次的路径。在这个地图中,每个岛屿有个正整数的旅游价值,我们要找一条旅游价值最大的汉密尔顿路。 假设有n个岛屿,汉密尔顿路C1C2...Cn的价值分三个部分,假设Ci岛屿的价值为Vi。 第一部分:所有岛屿价值之和; 第二部分:路径中相邻两个岛屿的价值之积,即对于路径中的CiCi+1,我们把ViVi+1加入总价值中; 第三部分:路径中相邻三个岛屿CiCi+1Ci+2,如果Ci和Ci+2之间有桥梁连接,则把Vi*Vi+1*Vi+2加入总价值。 要求计算汉密尔顿路的最大价值,以及方案数。

例13:Islands and Bridges 输入:第一行输入q(q<=20)表示测试数据个数。对于每个测试数据,第一行输入两个整数n(n<=13)和m,表示岛屿和桥梁的数量,下一行包含n个正整数,第i个数为Vi(0<Vi<=100),接下来m行,每行包含两个整数x y,表示岛屿x和岛屿y之间有桥梁连接,岛屿编号为1到n 输出:对于每个测试数据,输出两个用空格隔开的整数,第一个数表示汉密尔顿路的最大价值,第二个数表示达到该价值的路径方案数。如果不存在汉密尔顿路输出“0 0”。 注意:一条路径可以以相反的方向去行走,我们认为这两条路径是同一条路径。 样例输入: 样例输出: 2 22 3 3 3 69 1 2 2 2 1 2 2 3 3 1 4 6 1 2 3 4 1 3 1 4 2 4 3 4

方法一 搜索 时间复杂度O(N!),13!=6227020800,超时

方法二 状态压缩DP 用F[i,j,s]表示以i、j两个岛屿开头,岛屿经过情况为s,走完s中未经过的岛屿所能获得的最大价值。 通过考虑紧跟在i,j后面的岛屿k得到以下状态转移方程 F[i,j,s]=max(Vk*Vj+Vi*Vj*Vk(if a[i,k]=1)+F[j,k,s']) 其中k满足(a[j,k]=1)and(s and (1 shl(k-1))<>0),s1’=s xor(1 shl(k-1)) 时间复杂度为O(N3*2N),最坏17997824

例14:最大利润 政府邀请了你在火车站开饭店,但不允许同时在两个相连接的火车站开。任意两个火车站有且只有一条路径,每个火车站最多有50个和它相连接的火车站。 告诉你每个火车站的利润,问你可以获得的最大利润为多少。 例如下图是火车站网络: 最佳投资方案是在1,2,5,6这4个火车站开饭店可以获得利润为90

例14:最大利润 输入:第一行输入整数N(<=100000),表示有N个火车站,分别用1,2。。。,N来编号。接下来N行,每行一个整数表示每个站点的利润,接下来N-1行描述火车站网络,每行两个整数,表示相连接的两个站点。 输出:输出一个整数表示可以获得的最大利润。 样例输入: 样例输出: 6 90 10 20 25 40 30 4 5 1 3 3 4 2 3 6 4

分析 任意两个火车站有且只有一条路径,是一棵树。 我们用f[x]表示在以x为根的树中开饭店,x一定要开,所能获得的最大利润,g[x] 表示在以x为根的树中开饭店,x一定不开,所能获得的最大利润,如果x开,则它的儿子们y1,…yk一定不能开,如果x不开,它的儿子们可开可不开,于是得到以下状态转移方程: f[x]= +a[x] (yi是x的儿子) g[x]= (yi是x的儿子) 实现时用dfs去实现,每个点只需求一次,所以时间复杂度为O(N)。

例15:选课 大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分。学生最后的学分是他选修的各门课的学分的总和。 每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如,《数据结构》必须在选修了《高级语言程序设计》之后才能选修。我们称《高级语言程序设计》是《数据结构》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。为便于表述每门课都有一个课号,课号依次为1,2,3,……。

例15:选课 下面举例说明 例子中1是2的先修课,即如果要选修2,则1必定已被选过。同样,如果要选修3,那么1和2都一定已被选修过。 学生不可能学完大学所开设的所有课程,因此必须在入学时选定自己要学的课程。每个学生可选课程的总数是给定的。现在请你找出一种选课方案,使得你能得到学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

例15:选课 输入: 输入文件的第一行包括两个正整数M、N(中间用一个空格隔开)其中M表示待选课程总数(1≤M≤1000),N表示学生可以选的课程总数(1≤N≤M)。 以下M行每行代表一门课,课号依次为1,2……M。每行有两个数(用一个空格隔开),第一个数为这门课的先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。 输出: 输出文件第一行只有一个数,即实际所选课程的学分总数。以下N行每行有一个数,表示学生所选课程的课号。

分析 根据“每门课的直接选修课最多只有一门”这个条件可以知道该题目的模型是由若干棵树构成的森林。 如下左边为输入数据,右图为输入数据对应的森林。 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 1 5 7 3 4 6

方法一 首先添加一个0号结点,让森林中的所有树根都成为0号结点的儿子,从而把森林转化为一棵树。如下图所示: 2 1 5 7 3 4 6 2 2 1 5 7 3 4 6

方法一 定义f[i,j]表示在以i为根的子树中选修j门课程所能获得的最大学分。 f[i,0]=0; 时间复杂度极高,超时。

方法二 方法一之所以超时,是由于“树”,树的儿子数量可能比较多,这样转移花费大量时间,我们可以把树等价转变为二叉树。 左儿子右兄弟法:结点的第一个儿子作为左儿子,右兄弟作为右儿子。 2 1 5 7 3 4 6

方法二 f[i,j]表示在以i为根的子树中选修j门课程能获得的最多学分。 根据i选不选分两种情况: 1.不选:那i及i的左子树都不能选,全部在i的右子树中选,f[rightson,j]; 2.选:i选,i的左子树可以选也可以不选,假设左子树选k个,则另外j-1-k在右子树中选,对应的状态为f[leftson,k]+f[rightson,j-1-k]; 用自顶向下记忆化搜索实现,时间复杂度为O(mn2)。

方法三 不需要转变成二叉树。 f[i,j]表示在以i为根的子树中选修j门课程能获得的最多学分。 方法一的方法采用自顶向下,所以复杂度很高,我们可以考虑用自下向上。 fa[i]存储i的父结点,我们把fa[i]的儿子们看作是个个物品,当我们计算完f[i,j]后把(j,f[i,j])看作是背包中的一个物品,其中j是体积,f[i,j]是价值,用这个物品去更新f[fa[i],k](k>=j)更新的方法跟背包问题一样。程序段如下: for k:=n downto j do if f[fa[i],k-j]+f[i,j]>f[fa[i],k] then f[fa[i],k]:=f[fa[i],k-j]+f[i,j]; 用BFS得到一个序列,从后往前执行更新。 时间复杂度为O(m*n2)。

谢谢!