算法--动态规划

来源:互联网 发布:64码高清网络电视官方 编辑:程序博客网 时间:2024/06/05 19:46

有向无环图中的最长简单路径

问题:

给定一个有向无环图G=(V,E),边权重为实数,给定图中两个顶点s和t。求从s到t的最长加权简单路径。

LWP1

转换成拓扑图如下:

LWP2

用LWP(u,v)表示从点u到点v的最长简单路径,W(t,v)表示边tv的权重,adj(v)表示点v的前驱节点的集合,则:

LWP(u,v)=0; u==v;

LWP(u,v)=max(LWP(u,t)+W(t,v)); u!=v;t∈adj(v);

即从点u到点v的最长简单路径是从点u到点v前驱节点t的最长简单路径加上tv权重的和的最大值。

最长回文子序列

问题:

求给定输入字符串的最长回文子序列(子序列不要求连续)。

用LPS(i,j)表示从字符串第i个字符到第j个字符的最长回文子序列的长度,字符串的长度为n,则要求LPS(1,n),则:

LPS(i,j)=0; i>j;

LPS(i,j)=1; i==j;

LPS(i,j)=LPS(i+1,j-1)+2; str[i]==str[j];

LPS(i,j)=max(LPS(i+1,j),LPS(i,j-1)); str[i]!=str[j];

双调欧几里得旅行商问题

问题:

给定平面上n个点作为输入,求出连接所有n个点的最短巡游路线。

J.L.Bentley建议将问题简化,限制巡游路线为双调巡游,即从最左边的点开始,严格向右前进,直至最右边的点,然后调头严格向右前进,直至回到起始点。

设计一个O(n^2)时间的最优双调巡游路线算法,可以认为任何两点的x坐标均不同,且所有实数运算都花费单位时间(提示:从左至右扫描,对巡游路线的两个部分分别维护可能的最优解)。

OJLD

  1. 将n个点按x坐标从小到大排序,时间复杂度为O(nlogn);
  2. 用PATH(i,j)表示从点i严格向左到点1,再严格向右到点j的最短路径,路径包括从点1到点max(i,j)的所有点,因为PATH(i,j)==PATH(j,i),所以只考虑i>=j时的情况;dir(i,j)表示从点i到点j的直接路径;
  3. 当i>j+1时,点i-1一定是小于i的最大节点,i-1>j,所以PATH(i,j)=PATH(i-1,j)+dir(i,i-1); i>j+1;
  4. 当i==j+1时,此时点i可能与小于i的所有节点相连,所以PATH(i,j)=min(PATH(k,j)+dir(i,k); k∈{x|x < i};
  5. 初始值PATH(2,1)=dir(2,1),计算PATH(i,j);

    for(int i=3;i<=n;i++)    for(int j=1;j<i;j++)        if(i>j+1)           //case 1            PATH[i][j]=PATH[i-1][j]+dir[i][i-1];        else if(i==j+1){    //case 2            int min=∞;            for(int k=1;k<i;k++)                if (PATH[k][j]+dir[i][k]<min)                    min=PATH[k][j]+dir[i][k];            PATH[i][j]=min;        }
  6. 计算PATH(n,n)=min(PATH(n,k)+dir(n,k));

整齐打印

问题:

考虑整齐打印问题,即在打印机上用等宽字符打印一段文本。输入文本为n个单词的序列,单词长度分别为l1,l2,……,ln个字符。要求将此段文本整齐打印在若干行上,每行最多M个字符。若每行包含第i到底j(i<=j)个单词,且单词间隔为一个空格符,则行尾的额外空格符数量为M-j+i-(li+……+lj),此值必须为非负的,否则一行内无法容纳这些单词。要求能最小化所有行(除最后一行)的额外空格数。

用MES(i)表示从第1到第i个单词的最小额外空格数,W(i,j)表示第i到第j个单词写入一行时该行的额外空格数,则:

MES(i)=0; i==0;

MES(i)=min(MES(k-1)+W(k,i)); 1<=k<=i且W(k,i)>=0;

编辑距离

问题:
编辑距离,又称Levenshtein距离,指两个字符串之间,由一个转换成另一个所需的最少编辑次数。有效的编辑操作为单个字符的替换、插入和删除。

用EDIT(i,j)表示长度为i的字符串转换成长度为j的字符串的编辑距离,f(i,j)表示第一个字符串的第i个字符和第二个字符串的第j个字符是否相同,若相同则f(i,j)=0,否则f(i,j)=1,则:

EDIT(i,j)=i; j==0;

EDIT(i,j)=j; i==0;

EDIT(i,j)=min(EDIT(i-1,j)+1,EDIT(i,j-1)+1,EDIT(i-1,j-1)+f(i,j)); i>0且j>0;

公司聚会计划

问题:

Stewart教授是一家公司总裁的顾问,这家公司正在计划一个公司的聚会。这个公司有一个层次式的结构——管理关系形成一颗以总裁为根的树。人事部门按每个员工喜欢聚会的程度来排名,排名是一个实数。为了使每个参加聚会者都喜欢这个聚会,总裁不希望一个雇员和她的直接上司同时参加。Stewart教授面对一颗描述公司结构的树,使用了左孩子右兄弟描述法,树中每个节点除了包含指针,还包含雇员的名字以及雇员喜欢聚会的排名。描述一个算法,它生成一张客人列表,使得客人喜欢聚会的程度的总和最大。

用sel(i)表示节点为i的员工参加聚会时以节点i为根的子树的喜欢程度,unsel(i)表示节点为i的员工不参加聚会时以节点i为根的子树的喜欢程度,data(i)表示节点i喜欢聚会的排名,join(i)记录节点i是否参加聚会,则:

  1. 当节点i不参加聚会时,节点i的下属可以选择参加或者不参加聚会,所以unsel(i)=sum(max(sel(c),unsel(c))); 节点c为节点i的子节点;
  2. 当节点i参加聚会时,节点i的下属不能参加聚会,所以sel(i)=enjoy(i)+sum(unsel(c)); 节点c为节点i的子节点;

基于接缝裁剪的图像压缩

问题:

给定一副彩色图像,它由一个 m * n 的像素数组 A[1..m,1..n] 构成,每个像素是一个红绿蓝(RGB)亮度的三元组。假定我们希望轻度压缩这幅图像。具体地,我们希望从每一行中删除一个像素,使得图像变窄一个像素。但为了避免影响视觉效果,我们要求相邻两行中删除的像素必须位于同一列或相邻列。也就是说,删除的像素构成从顶端行到底端行的一条“接缝”(seam),相邻像素均在垂直或对角线方向上相邻。

假定现在对每个像素 A[i,j] 我们都已计算出一个实型的“破坏度” d[i,j] ,表示删除像素 A[i,j] 对图像可视效果的破坏程度。直观地,一个像素的破坏度越低,它与相邻像素的相似度越高。再假定一条接缝的破坏度定义为包含的响度的破坏度之和,求破坏度最低的接缝。

用DES[i,j]记录接缝从第一行走到当前像素点(i,j)的最低破坏度,DIR[i,j]记录当前结点来自于上一行的方向(用-1,0,1分别表示左上,正上和右上方向)。

  1. DES[i,j]=d[i,j]; i==1
  2. DES[i,j]=d[i,j]+min(DES[i-1,j-1],DES[i-1,j],DES[i-1,j+1]); i>1; 记录DIR[i,j];
  3. DES[i,j]=min(i,k); i==m, 1<=k<=n; 从DIR[i,k]逆推就能得到“接缝”;

字符串拆分

问题:

某种字符串处理语言允许程序员将一个字符串拆分为两段。由于此操作需要复制字符串,因此要花费n个时间单位来将一个n个字符的字符串拆为两段。假定一个程序员希望将一个字符串拆分为多段,拆分的顺序会影响所花费的总时间。例如,假定这个程序员希望将一个20个字符的字符串在第2个,第8个以及第10个字符后进行拆分(字符由左至右,从1开始升序编号)。如果她按由左到右顺序进行拆分,则第一次拆分花费20个时间单位,第二次拆掉分花费18个时间单位(在第8个字符处拆分3-20间的字符串),而第三次拆分花费12个时间单位,共花费50个时间单位。但如果她按由右至左的顺序进行拆分,第一次拆分花费12个时间单位,第二次拆分花费10个时间单位,而第三次拆分花费8个时间单位,共花费38个时间单位。还可以按其他顺序,比如,她可以首先在第8个字符处进行拆分(时间20),接着在左边一段第2个字符处进行拆分(时间8),最后在右边一段第10个字符处进行拆分(时间12),总时间为40。

设计算法,对给定的拆分位置,确定最小代价的拆分顺序,更形式化地,给定一个n个字符的字符串S和一个保存m个拆分点的数组L[1…m],计算拆分的最小代价,以及最优拆分序列。

用COST[i,j]表示子串str(i,j)的最小拆分代价,BREAK[i,j]表示子串str(i,j)的第一个拆分点,若没有拆分点则记BREAK[i,j]=0;

  1. 若子串str(i,j)中至多有一个拆分点,则COST[i,j]=j-i+1; BREAK[i,j]=0;
  2. 若子串str(i,j)中至少有两个拆分点,则COST[i,j]=j-i+1+min(COST[i,L[k]]+COST[L[k]+1,j]); L[k]∈落在子串str(i,j)中的拆分点; BREAK[i,j]=L[k];

投资策略规划

问题:

你所掌握的算法知识帮助你从Acme计算机公司获得了一份令人兴奋的工作,签约奖金1万美元。你决定利用这笔钱进行投资,目标是10年后获得最大回报。你决定请 Amalgamated 投资公司管理你的投资,该公司的投资回报规则如下:该公司提供n种不同的投资,从1~n编号。在第j年年底,若你选择第i中方案,你会得到 d * r[i,j] 美元。回报率是有保证的,即未来10年每种投资的回报率均已知。你每年只能做出一次投资决定。在每年年底,你既可以将钱继续投入到上一年选择的投资种类中,也可以转移到其他投资中(转移到已有的投资种类,或者新的投资种类)。如果跨年时你不做投资转移,需要支付f1美元的费用,否则,需要支付f2美元的费用,其中f2>f1。

本问题允许你每年将钱投入到多种投资中,求最优投资策略。

用REWARD[j]表示第j年的最大收益,TYPE[j]表示第j年选择的投资方案;

  1. 第一年,REWARD[j]=10000 * max(r[k,j]); 1<=k<=n; TYPE[j]=k;
  2. 后一年,若选择方案和去年相同,W1=REWARD[j-1] * r[TYPE[j-1],j] - f1;若选择方案和去年不同,W2=REWARD[j-1] * max(r[k,j]) - f2;所以,REWARD[j]=max(W1,W2);记录TYPE[j];

签约棒球自由球员

问题:

假设你是一支棒球大联盟球队的总经理。在赛季休季期间,你需要签入一些自由球员。球队老板给你的预算为X美元,你可以使用少于X美元来签入球员,但如果超支,球队老板就会解雇你。

你正在考虑在N个不同位置签入球员,在每个位置上,有P个该位置的自由球员供你选择。由于你不希望任何位置过于臃肿,因此每个位置最多签入一名球员(如果在某个特定位置上你没有签入任何球员,则意味着计划继续使用现有球员)。

为了确定一名球员的价值,你决定使用一种称为“VORP”,或“球员替换价值”的统计评价指标。球员的VORP值越高,其价值越高。但VORP值高的球员签约费用并不一定比VORP值低的球员高,因为还有球员价值之外的因素影响签约费用。

对于每个可选择的自由球员,你知道他的三方面信息:

  1. 他打哪个位置。
  2. 他的签约费用。
  3. 他的VORP。

设计一个球员选择算法,是的总签约费用不超过X美元,而球员的总VORP最大。你可以假定每位球员的签约费用是10万美元的整数倍。算法应输出签约球员的总VORP值,总签约费用,以及球员名单。分析算法的时间和空间复杂度。

这明显是一个改进的背包问题。不超过X美元,相当于背包的总容量。总VORP最大,相当于背包的总价值。每位球员的签约费用,相当于每件物品的重量。现在我们要给出在签约费用不超过X美元的前提下使总价值最大的球员选择方案。以0-1背包为背景的动态规划算法,我们可以给其改进一下,这里每个位置(相当于每件物品)有P个球员。那么我们要多加一个循环用于求在找到最大价值方案的前提下还要先找出P个球员之中的价值最大的那个。那么我们现在可以刻画出递归式:

  1. VORP[i,x]=max(p.vorp); p∈S(N,x); i==N;
  2. VORP[i,x]=max(VORP[i+1,x],max(p.vorp+VORP[i+1,x-p.cost])); p∈S(i,x); i < N;
0 0