标签:dp
题目一
描述
关键子工程(project.c/cpp/pas)
在大型工程的施工前,我们把整个工程划分为若干个子工程,并把这些子工程编号为1、2、……、N;这样划分之后,子工程之间就会有一些依赖关系,即一些子工程必须在某些子工程完成之后才能施工。由于子工程之间有相互依赖关系,因此有两个任务需要我们去完成:首先,我们需要计算整个工程最少的完成时间;同时,由于一些不可预测的客观因素会使某些子工程延期,因此我们必须知道哪些子工程的延期会影响整个工程的延期,我们把有这种特征的子工程称为关键子工程,因此第二个任务就是找出所有的关键子工程,以便集中精力管理好这些子工程,尽量避免这些子工程延期,达到用最快的速度完成整个工程。为了便于编程,现在我们假设:
(1)根据预算,每一个子工程都有一个完成时间。
(2)子工程之间的依赖关系是:部分子工程必须在一些子工程完成之后才开工。
(3)只要满足子工程间的依赖关系,在任何时刻可以有任何多个子工程同时在施工,也既同时施工的子工程个数不受限制。
(4)整个工程的完成是指:所有子工程的完成。输入数据
第1行为N,N是子工程的总个数,N≤200。
第2行为N个正整数,分别代表子工程1、2、……、N的完成时间。
第3行到N+2行,每行有N-1个0或1。其中的第I+2行的这些0,1,分别表示“子工程I”与子工程1、2、…、I-1、I+1、…N的依赖关系,(I=1、2、……、N)。每行数据之间均用一个空格分开。输出数据:
如子工程划分不合理,则输出-1;
如子工程划分合理,则用两行输出:第1行为整个工程最少的完成时间。第2行为按由小到大顺序输出所有关键子工程的编号。
分析
如果子工程 A 必须在 B 之后完成, 那么从 B->A 连边。然后拓扑排序,如果出现环,说明任务无法完成。边拓扑排序边边计算这个结点之前完成的最小时间。记录它之前最后一个完成的工程和时间。
找到最后完成的任务,根据之前记录的它之前最后一个完成的工程一层层找到最后一个任务,也就是一条最长路。
- 但是,最长路可能并不只一条,所以可以用 vector 记录所有的最长路。我第一次尝试这样做。详见代码。
- 后来看题解发现可以在最后去找关键路径而不是在一开始拓扑排序就记录关键路径。这样可以省很多事。
代码
题目二
描述
机器分配(machine.c/cpp/pas)
某总公司拥有高效生产设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为总公司提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。
分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。其中M<=100,N<=100。输入数据:
第一行为两个整数M,N。接下来是一个N×M的矩阵,其中矩阵的第i行的第j列的数Aij表明第i个公司分配j台机器的盈利。所有数据之间用一个空格分隔。输出数据:
只有一个数据,为总公司分配这M台设备所获得的最大盈利。
分析
- DP
f[i][j]
表示前 i 个公司分到 j 台机器的最大盈利f[i][j] = max{f[i][j], f[i-1][j-k] + A[i][k](0≤k≤j)}
f[n][m]
代码
题目三
描述
编辑距离(edit.c/cpp/pas)
字符串是数据结构和计算机语言里很重要的数据类型,在计算机语言中,对于字符串我们有很多的操作定义,因此我们可以对字符串进行很多复杂的运算和操作。实际上,所有复杂的字符串操作都是由字符串的基本操作组成。例如,把子串a替换为子串b,就是用查找、删除和插入这三个基本操作实现的。因此,在复杂字符串操作的编程中,为了提高程序中字符操作的速度,我们就应该用最少的基本操作完成复杂操作。
在这里,假设字符串的基本操作仅为:删除一个字符、插入一个字符和将一个字符修改成另一个字符这三种操作。
我们把进行了一次上述三种操作的任意一种操作称为进行了一步字符基本操作。
下面我们定义两个字符串的编辑距离:对于两个字符串a和b,通过上述的基本操作,我们可以把a变成b或b变成a;那么,把字符串a变成字符串b需要的最少基本字符操作步数称为字符串a和字符串b的编辑距离。
例如,如a=“ABC”,b=“CBCD”,则a与b的编辑距离为2。
你的任务就是:编一个最快的程序来计算任意两个字符串的编辑距离。输入数据:
第1行为字符串a;第2行为字符串b。注:字符串的长度不大于1000,字符串中的字符全为大写字母。输出数据:
编辑距离。
分析
- DP
f[i][j]
表示 a 的前 i 个字符和 b 的前 j 个字符匹配的最小编辑距离- 初始化:
f[i][0] = i
// 表示将前 i 个全部删除
f[0][i] = i
// 表示在 a 中插入 b 的前 i 个- 修改
f[i][j] = f[i-1][j-1] + (A[i-1] == B[j-1] ? 0 : 1)
// A, B 是字符串,以 0 开始做下标- 插入
f[i][j] = min(f[i][j], f[i][j-1] + 1)
删除
f[i][j] = min(f[i][j], f[i-1][j] + 1)
f[strlen(A)][strlen(B)]
代码
题目四
描述
在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资。我们应该注意到,人民币的硬币系统是100,50,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01元,采用这些硬币我们可以对任何一个工资数用贪心算法求出其最少硬币数。
但不幸的是:我们可能没有这样一种好的硬币系统,因此用贪心算法不能求出最少的硬币数,甚至有些金钱总数还不能用这些硬币找零。例如,如果硬币系统是40,30,25元,那么37元就不能用这些硬币找零;95元的最少找零硬币数是3。又如,硬币系统是10,7,5,1元,那么12元用贪心法得到的硬币数为3,而最少硬币数是2。
你的任务就是:对于任意的硬币系统和一个金钱数,请你编程求出最少的找零硬币数;如果不能用这些硬币找零,请给出一种找零方法,使剩下的钱最少。输入数据:
第1行,为N和T,其中1≤N≤500为硬币系统中不同硬币数;1≤T≤10000为需要用硬币找零的总数。
第2行为N个数值不大于65535的正整数,它们是硬币系统中各硬币的面值。输出数据:
如T能被硬币系统中的硬币找零,请输出最少的找零硬币数。
如T不能被硬币系统中的硬币找零,请输出剩下钱数最少的找零方案中的所需的最少硬币数。
分析
- 完全背包
f[i]
表示找钱数为 i 时最少的硬币数量。f[i] = min{f[i], f[i-A[j]] + 1(A[j]≤i)}
// 注意判断f[t]
- 如果
f[t] == INF
,从 t 向前找到第一个不为INF
的值f[t1]
,t - t1
就是找零后剩下的最小钱数。- 什么是找零后剩下的最小钱数?找回的钱比设定要少(觉得十分不合理…),让这个差值尽可能小
代码
- 改了好几遍才都改对,以上代码都可以 AC 了。
- 各种细节 :
- STL的
unique
并不真正把重复的元素删除,是把重复的元素移到后面去了。返回一个迭代器,它自己和其后的元素都是原来重复的元素- 数组越界的判定
- DP时的条件判定
INF = 0x3f3f3f3f
,memset(0x3f)
的写法可以放心用。致谢 Archon- 。。。
标签:dp
原文地址:http://blog.csdn.net/qq_21110267/article/details/43969275