算法设计策略II:动态规划

Report
Dynamic Programming
目录

DP适用问题第一特征:重叠子问题




DP适用问题第二特征:最优子结构



Fibonacci Number
Binomial Coefficients
Shortest paths in DAGs
Chain matrix multiplication
Longest Increasing Subsequences
运用DP解决问题的步骤
目录

DP经典问题





Knapsack 背包问题
硬币问题
树状DP
Edit Distance
The Partition Problem
简介

动态规划(Dynamic Programming, DP)是在20
世纪50年代由一位卓越的美国数学家Richcard
Bellman发明的。它作为一种重要的工具在应
用数学中被广泛的应用。它不仅可以解决特定
类型的优化问题,还可以作为一种通用的算法
设计技术为计算机服务。
DP适用问题的第一特征


所求解的问题具有重叠子问题。
解决方法


进行记忆化求解。(用空间换时间)
依照某种合适次序计算,以避免重复计算。
Fibonacci Number

求Fibonacci数:
f(n) = f(n-1) + f(n-2)
f(0)=0, f(1)=1
n>1
Fibonacci Number

常规递归
long long fib_r(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
return (fib_r(n-1) + fib_r(n-2));
}

指数级时间复杂度,无法忍受。
Fibonacci Number

常规递归

重叠子问题,导致重复计算。
Fibonacci Number

自顶而下,记忆化求解
long long fib_c(int n)
{
if (f[n] == -1)
f[n] = fib_c(n-1) + fib_c(n-2);
return (f[n]);
}
long long fib_c_driver(int n)
{
f[0] = 0;
f[1] = 1;
for (int i=2; i<=n; i++) f[i] = -1;
return (fib_c(n));
}
Fibonacci Number

自底而上,顺序求解
long long fib_dp(int n)
{
long long f[MAXN + 1];
f[0] = 0;
f[1] = 1;
for (int i=2; i<=n; i++)
f[i] = f[i-1] + f[i-2];
return (f[n]);
}
Fibonacci Number

自底而上,顺序求解。空间优化
long long fib_ultimate(int n)
{
/* back2,back1 are last two values of f[n] */
long long back2 = 0, back1 = 1;
long long next;
if (n==0) return 0;
for (int i=2; i<n; i++) {
next = back1 + back2;
back2 = back1;
back1 = next;
}
return (back1+back2);
}
Binomial Coefficients


C(n,k)表示从n个不同物体中选出k个的组合数
C(n,k)=n!/((n−k)!k!)=C(n-1,k-1)+C(n-1,k)
Pascal’s triangle
Matrix representation
Binomial Coefficients
Const int MAXN = 100;
long long binomial_coefficient(int n, int k)
{
long long bc[MAXN+1][MAXN+1];
for (int i = 0; i<=n; i++) bc[i][0] = 1;
for (int j = 0; j<=n; j++) bc[j][j] = 1;
for (int i = 1; i<=n; i++)
for (int j=1; j<i; j++)
bc[i][j] = bc[i-1][j-1] + bc[i-1][j];
return ( bc[n][k] );
}
Shortest paths in DAGs
以S为源点对左图中的所有结点进行拓扑排序,得到右图。
则计算从源点S到点D的最短路径可以表示为:
dist(D) = min{dist(B)+edge(BD), dist(C)+edge(CD)}
= min{dist(B)+1, dist(C)+3}
这里的B和C是结点D的两个前驱结点。
Shortest paths in DAGs
Topologically sort vertices in G;
Initialize all dist(.) values to INF
dist(s) = 0
for each v ∈ V , in linearized order do
dist(v) = min(u,v) ∈E { dist(u) + w(u,v) }
Example
求s到任意点的最短距离
6
r

5
s
0
2
t

1
u
7

–1
4
3
2
v

–2
w

Example
6
r

5
s
0
2
t

1
u
7

–1
4
3
2
dist[r] = , dist[s] = 0
v

–2
w

Example
6
r

5
s
0
2
t
2
1
u
7
6
–1
4
3
2
v

–2
w

Example
6
r

5
s
0
2
t
2
1
u
7
6
–1
v
6
–2
4
3
2
dist[t]=min{dist[s]+2, dist[r]+3}
w
4
Example
6
r

5
s
0
2
t
2
1
u
7
6
–1
v
5
–2
4
3
2
dist[u]=min{dist[s]+6, dist[t]+7}
w
4
Example
6
r

5
s
0
2
t
2
1
u
7
6
–1
v
5
–2
4
3
2
dist[v]=min{dist[t]+4, dist[u]-1}
w
3
Example
6
r

5
s
0
2
t
2
1
u
7
6
–1
v
5
–2
w
3
4
3
2
dist[w]=min{dist[t]+2, dist[u]+1, dist[v]-2}
动态规划术语

状态:贴切,简洁的描述出事物性质的单元量。例如:
Dist[x]。
要求:状态与状态之间可以转移,以便有初始状态逐渐转
移到目标状态,找到问题的解。

阶段:若干性质相近可以同时处理的状态的集合。就是计
算状态的顺序。
要求:每个阶段中状态的取值只与这个阶段之前的阶段中
的状态有关,与这个阶段之后的阶段中的状态无关。
动态规划术语

状态转移方程:前一个阶段中的状态转移到后一
个阶段的状态得演变规律,即相邻两个阶段的状
态变化方程。
Dk(i) = opt { Dk-1(j) + cost(i,j) }
第k阶段的i状态与第k-1阶段的j状态有关

决策:计算每个状态时作出的选择。
动态规划时间效率
动态规划解决问题的方法是通过解决很多小的问
题而解决大问题。
动态规划的效率取决于两个因素:子问题的数量
和子问题的解决效率。
时间效率:子问题的数量*子问题的时间效率。
DP适用问题的第二特征


所求解的问题具有最优子结构,即问题的一个
最优解中包含子问题的最优解。
例如,矩阵链乘法问题中,Ai×Ai+1×…×Aj的
一个最优加全部括号把乘积在和之间分裂,它
包含了两个子问题Ai×Ai+1×…×Ak和
Ak+1×Ak+2×…×Ak的最优解。
Chain matrix multiplication
Chain matrix multiplication
Chain matrix multiplication
For 1≤ i≤k≤j ≤n, define
C(i,j) = minimum cost of multiplying Ai×Ai+1×…×Aj
for i = 1 to n: C(i, i) = 0
for s = 1 to n - 1:
for i = 1 to n - s:
j=i+s
C(i, j) = min{C(i, k) + C(k + 1, j) +mi-1·mk·mj : i≤k ≤j}
return C(1, n)
The subproblems constitute a two-dimensional table, each of
whose entries takes O(n) time to compute. The overall
running time is thus O(n3).
Chain matrix multiplication


矩阵链相乘问题具有最优子结构,即问题的一
个最优解中包含子问题的最优解。
Ai×Ai+1×…×Aj的一个最优加全部括号把乘积
在和之间分裂,它包含了两个子问题
Ai×Ai+1×…×Ak和Ak+1×Ak+2×…×Ak的最优
解。
相关习题


sicily1345 能量项链
给出一串项链,每次可以选相邻两个珠子进行
聚合,释放出一定的能量,并产生一个新珠子
。项链是头尾相接的。求释放的能量的总和的
最大值。
项链长度不超过100。
Longest Increasing Subsequences
Longest increasing subsequence

Algorithm:
Longest Increasing Subsequences
L(0) = 1, P(.) = -1
For i = 1, 2, …, n
L(i) = 1 + max0<j<i{L(j)}, where Sj<Si
P(i) = j
L(i) is the length of the longest path ending at i (plus 1).
Time complexity is O(n2), the maximum being when the
input array is sorted in increasing order.
Position i
1
2
3
4
5
6
7
8
Sequence Si
Length Li
Predecessor Pi
5
1
-1
2
1
-1
8
2
1
6
2
1
3
2
2
6
3
5
9
4
6
7
4
6
Longest Increasing Subsequences
Position i
1
2
3
4
5
6
7
8
s[i]
5
2
8
6
3
6
9
7
l[i]
1
1
2
2
2
3
4
4
p[i]
-1
-1
1
1
2
5
6
6
相关习题

推荐题目:
sicily1060 Bridging Signals (需要用特殊法
做)
sicily1685 Missile(最长不单调子序列,数据
小,O(n2))
sicily1448 Antimonotonicity(最长不单调子序
列,数据大,O(n))
小结

动态规划的一般步骤





1.
2.
考察问题结构,给出状态表示方式,列出递归方程
(状态转移方程)
分析复杂度,若复杂度较高则需要优化。
用递推或记忆化搜索实现。
根据计算最优值过程中的信息,递归的构造一个最
优解。
应用动态规划的前提条件
重复子问题
最优子结构
经典问题
背包问题





01背包问题
完全背包问题
多重背包问题
分组的背包问题
有依赖的背包问题
01背包问题




有N件物品和一个容量为V的背包。第i件物品的费
用是c[i],价值是w[i]。求解将哪些物品装入背包
可使价值总和最大。
这是最基础的背包问题,特点是:每种物品仅有
一件,可以选择放或不放。
状态:用f[i][v]表示前i件物品恰放入一个容量为v
的背包可以获得的最大价值,其状态转移方程为
f[i][v]=max{ f[i-1][v], f[i-1][v-c[i]]+w[i] }。
复杂度分析:


状态数:O(NV), 迁移:O(1)
总复杂度:O(NV)


状态f[i][v]表示前i件物品恰放入一个容量为v的背包
可以获得的最大价值。
对于“将前i件物品放入容量为v的背包中”这个子
问题,若只考虑第i件物品的策略(放或不放),那
么就可以转化为一个只牵扯前i-1件物品的问题。



如果不放第i件物品,那么问题就转化为“前i-1件物品放
入容量为v的背包中”,价值为f[i-1][v];
如果放第i件物品,那么问题就转化为“前i-1件物品放入
剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就
是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
状态转移方程:
f[i][v]=max{ f[i-1][v], f[i-1][v-c[i]]+w[i] }



时间复杂度O(NV)
由于计算f[i][v]时只用到f[i-1][v]和f[i-1][v-c[i]],在
每次主循环中我们以v=V..0的顺序推f[v],这样
就保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的
值,f[v]保存的是状态f[i-1][v]的值。
伪代码如下:
for i=1..N
for v=V..0
f[v]=max{f[v], f[v-c[i]] + w[i]};

空间复杂度成功降到O(V)

抽象成过程ZeroOnePack,表示处理一件01背
包中的物品,两个参数cost、weight分别表明这
件物品的费用和价值。
void ZeroOnePack(cost,weight){
for v=V..cost
f[v]=max{f[v],f[v-cost]+weight}
}

01背包问题的伪代码就可以这样写:
for i=1..N
ZeroOnePack(c[i],w[i]);

初始化问题



如果题目要求“恰好装满背包”时的最优解
在初始化时除了f[0]为0其它f[1..V]均设为-∞
如果并没有要求必须把背包装满,初始化时应该将f[0..V]
全部设为0
初始化的f数组事实上就是在没有任何物品可以放入背包
时的合法状态。如果要求背包恰好装满,那么此时只有容
量为0的背包可能被价值为0的nothing“恰好装满”,其它
容量的背包均没有合法的解,属于未定义的状态,它们的
值就都应该是-∞了。如果背包并非必须被装满,那么任何
容量的背包都有一个合法解“什么都不装”,这个解的价
值为0,所以初始时状态的值也就全部为0了。

推荐题目:
sicily1782 Knapsack
sicily1146 采药
sicily1342 开心的金明
完全背包问题



有N种物品和一个容量为V的背包,每种物品
都有无限件可用。第i种物品的费用是c[i],价
值是w[i]。求解将哪些物品装入背包可使这些
物品的费用总和不超过背包容量,且价值总和
最大。
状态f[i][v]表示前i件物品恰放入一个容量为v的
背包可以获得的最大价值,其状态转移方程为
f[i][v]=max{ f[i-1][v], f[i][v-c[i]]+w[i] }
时间复杂度O(NV)
完全背包问题

O(VN)的算法
空间优化,伪代码:
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-c[i]]+w[i]}
void CompletePack(int cost,int weight)
{
for(int v=cost;v<=V;++v)
f[v] >?= f[v-cost]+weight;
}


01背包按照v=V..0的逆序来循环,要保证第i次
循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。
换句话说,这正是为了保证每件物品只选一次,
保证在考虑“选入第i件物品”这件策略时,依
据的是一个绝无已经选入第i件物品的子结果f[i1][v-c[i]]。
完全背包的特点恰是每种物品可选无限件,所以
在考虑“加选一件第i种物品”这种策略时,却
正需要一个可能已选入第i种物品的子结果f[i][vc[i]],所以就可以并且必须采用v=0..V的顺序循
环。即f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}
二维背包


对于每件物品,具有两种不同的费用;选择这件
物品必须同时付出这两种代价;对于每种代价都
有一个可付出的最大值(背包容量). 问怎样选择
物品可以得到最大的价值. 设第i件物品所需的两
种代价分别为a[i]和b[i]. 两种代价可付出的最大值
(两种背包容量)分别为V和U. 物品的价值w[i].
分析:费用加了一维,只需状态也加一维即可. 设
f[i][v][u]表示前i件物品付出两种代价分别为v和u时
可获得的最大价值。状态转移方程就是:
f[i][v][u]=max{ f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i] }
二维背包


二维背包很容易扩展到多维背包
“二维费用”的条件有时是以这样一种隐含的
方式给出的:最多只能取M件物品. 这事实上
相当于每件物品多了一种“件数”的费用,每
个物品的件数费用均为1,可以付出的最大件
数费用为M.
多重背包问题


有N种物品和一个容量为V的背包。第i种物品
最多有n[i]件可用,每件费用是c[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的费用
总和不超过背包容量,且价值总和最大。
转化为二维背包求解。

推荐题目
poj1014 Dividing
sicily1077 Cash Machine(可能不算多重背包,
不过可采用类似方法)
分组背包


有N件物品和一个容量为V的背包。第i件物品的费
用是c[i],价值是w[i]。这些物品被划分为若干组,
每组中的物品互相冲突,最多选一件。求解将哪
些物品装入背包可使这些物品的费用总和不超过
背包容量,且价值总和最大。
分析:这个问题变成了每组物品有若干种策略:
是选择本组中的某一件,还是一件都不选。设
f[k][v]表示前k组物品花费费用v能取得的最大权值,
则有状态转移方程:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i] | 物品i属于组k}


使用一维数组的伪代码如下:
for 所有的组k
for v=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}
注意这里的三层循环的顺序
“for v=V..0”这一层循环必须在“for 所有的i属
于组k”之外。这样才能保证每一组内的物品最
多只有一个会被添加到背包中

推荐题目:
sicily1750 运动会
有依赖的背包问题


物品间存在某种“依赖”的关系,i依赖于j,表示若
选物品i,则必须选物品j。设没有某个物品既依赖于
别的物品,又被别的物品所依赖;另外,没有某件
物品同时依赖多件物品。
可以对主件i的“附件集合”先进行一次01背包,得
到费用依次为0..V-c[i]所有这些值时相应的最大价值
f'[0..V-c[i]]。那么这个主件及它的附件集合相当于Vc[i]+1个物品的物品组,其中费用为c[i]+k的物品的
价值为f'[k]+w[i]。也就是说原来指数级的策略中有
很多策略都是冗余的,通过一次01背包后,将主件i
转化为V-c[i]+1个物品的物品组,分组背包。

推荐题目:
sicily1346 金明的预算方案
推荐资料


Tianyi Cui《背包九讲》,可在课程页面上下
载。
推荐书籍:刘汝佳等《算法艺术与信息学竞赛
》(俗称“黑书”)1.5节 动态规划
最大字段和问题

最大子段和问题:
给定由n个整数(可能为负整数)组成的序列
j
a1,a2,…,an,求该序列形如的  a[k ]子段和的
k i
最大值。


设bi为到ai截至且包括ai的字段最大和。
bi = bi-1 + ai
bi-1>0
ai
bi-1<=0
b1 = a1
ans = max{ bi }
ans=b=a[1];
for(i=2;i<=n;i++)
{
if (b>0) b+=a[i]; else b=a[i];
if (b>ans) ans=b;
}


引申问题:
最大子矩阵和问题
最大m段子段和问题
推荐题目:
poj1050 To the Max(最大子矩阵和问题)
sicily1091 Maximum Sum(最大m段子段和问
题)
sicily1888 Circular Sequence (求一次最大
MAX,判断是否是整个串,如果是就求一次最
小MIN,然后MAX-MIN就是最后解,否则MAX
就是解)
硬币问题1




有n种硬币,每种硬币的面值为c[i]元,且只有
一枚,问用这n种硬币找零s元的方法数。
设d[i][j]为前i种硬币组成j元的方法数。
d[i][j] = d[i-1][j] + d[i-1][ j-c[i] ]
d[0][0]=1,d[0][1…s]=0
若用top_down实现,空间复杂度为O(n2) 。
若用bottom_up实现,空间复杂度可为O(n) 。
硬币问题1
d[0]=1; d[1…s]=0;
for (i=1; i<=n; i++)
{
for (j=s; j>=c[i]; j--)
{
d[j] += d[j-v[i]];
}
}
硬币问题2

有n种硬币,每种硬币的面值为c[i] 元,有m[i]
枚,问用这n种硬币找零s元的方法数。
d[0]=1;d[1…s]=0;
for (i=1; i<=n; i++) {
for (j=s; j>=c[i]; j--){
for(k=1;k<=m[i];k++){
if (j-k*c[i]>=0)
d[j] += d[j-k*c[i]];
else break;
}
}
}
硬币问题3

有n种硬币,每种硬币的面值为c[i]元,有无数
枚,问用这n种硬币找零s元的方法数。
d[0]=1;d[1…s]=0;
for (i=1; i<=n; i++)
{
for (j=s; j>=c[i]; j--)
{
for(k=1;;k++)
{
if (j-k*c[i]>=0) d[j] += d[j-k*c[i]];
else break;
}
}
}
硬币问题3
d[0]=1; d[1…s]=0;
for (i=1; i<=n; i++)
{
for (j=c[i];j<=s; j++)
{
d[j] += d[j-c[i]];
}
}
硬币问题4


有n种硬币,面值分别为v[1], v[2], …, v[n],每种
有无限多。给定非负整数s, 可以选用多少个硬币,
使得面值之和恰好为s? 输出硬币数目的最小值和
最大值。1 ≤ n ≤ 100, 0 ≤ s ≤ 10000, 1 ≤ v[i] ≤ s.
问题可转化为DAG上的路径问题。把每种面值看
做一个点,表示“还需要凑够的面值”,则初始
状态为s,目标状态为0。若当前在状态i,每使用
一个硬币j,状态便转移到i-v[j].
硬币问题4

以d(i)记“从结点i出发到结点0的最长路径长度”。
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
int dp(int s)
{
if(vis[s]) return d[s];
vis[s] = 1;
int& ans = d[s];
ans = -INF; // INF = 1<<30
for(int i=1;i<=n;i++)
if(s>=v[i]) ans >?= dp(s-v[i]) + 1;
return ans;
}
硬币问题4

输出字典序最小的方案。
void print_ans(int *d, int s)
{
for(int i=1; i<=n; i++)
if (s>=v[i] && d[s]==d[s-v[i]]+1)
{
printf(“%d “, i);
print_ans(d, s-v[i]);
break;
}
}
硬币问题4

递推的写法,同时求min和max。
min[0] = max[0] = 0;
for(int i=1; i<=s; i++) {
min[i]=INF; max[i]=-INF;
}
for(int j=1; j<=n; j++)
for(int i=1; i<=s; i++)
if( i>=v[j]) {
min[i] <?= min[i-v[j]] + 1;
max[i] >?= max[i-v[j]] + 1;
}
cout << min[s] << “ “ << max[s] << endl;
相关习题
sicily 2014 Dairy Queen
sicily1005 Roll Playing Games(此题为搜索
题,里面得先dp一次)
sicily1564 HOUSING(可看成硬币问题)
sicily1902 Counting Problem
数字三角形问题

给定一个具有N层的数字三角形,从顶至底有
多条路径,每一步可沿左斜线向下或沿右斜线
向下,路径所经过的数字之和为路径得分,请
求出最小路径得分。
2
6
1
1
2
8
4
5
6
8
数字三角形
数字三角形问题





用二元组D(x,y)描述问题,D(x,y)表示从第x层第
y个位置到达底层的最小路径得分。
最优子结构性质:显然,D(x,y)的最优路径
Path(x,y)一定包含子问题D(x+1,y)或D(x+1,y+1)
的最优路径
递归关系:
D(x,y)=min{D(x+1,y),D(x+1,y+1)}+a(x,y)
D(n,k)=a(n,k),k=1,…,n
其中,a(x,y)为第X层第y个位置的数值。
D(x,y)表示从第X层第y个位置到达底层的最小
路径得分。原问题的最小路径得分即为D(1,1)
相关习题

sicily1563 GECKO
凸多边形的三角形剖分
给定一个凸多边形P={v0,v1,…vn-1} ,以及定义
在由凸多边形的边和弦组成的三角形上的权函
数w。要求确定该凸多边形的一个三角划分,使
得该三角划分中诸三角形上权之和为最小。
凸多边形的三角形剖分

最优子结构

状态转移方程
t[i,i]=0
t[i,j] = mini<=k<j{t[i,k]+t[k,j]+w(vivkvj)}
参考题目:HOJ 1714 Minimax Triangulation
sicily 1822 决斗

编号从1-n的n个人按逆时针方向排成一圈,他们
要决斗n-1场,其中第i个人与第i+1个人决斗(如
果i=n则与第一个人决斗),死者退出圈子,紧靠
死者右边的人成为与赢者直接相邻的人。任意两
人之间决斗的胜负都将在一矩阵中给出(如果
A[i,j]=1,则i与j决斗时,i总是赢,如果A[i,j]=0,
则i总是输),求所有可能赢得整场决斗的人的序
号
决斗



分析:假设需要判断x是否能赢得整场决斗,把环看成链,x
点拆成两个,那编号为x的人能从所有人中胜出的充分必要
条件是他能与自己相遇,这样在连续几个人的链中,只需考
虑头尾两个人是否能胜利会师,中间的则不予考虑,设
meet[i,j]记录i和j是否相遇,能为1,否则为0,问题转化为能
否找到一个k,使得i和k能相遇,k和j能相遇,且i或者j能够
打败k。
状态转移方程:
存在i<k<j使得meet[i,k]且meet[k,j]且(A[i,k]或A[j,k]),则
meet[i,j]=1,否则meet[i][j]=0。
时间复杂度O(n3),空间复杂度O(n2)。
树形动态规划




树型dp,即在树结构上做dp。由于树的结构本
身是一种递归结构,比较简单,单路连通,具
有很优的状态转移结构。
很多在一般图上是NP难的问题,在树结构上
存在多项式时间的DP算法。如图着色问题,
最小点覆盖问题。
必要条件:子树之间不可以相互干扰,如果本
来是相互干扰的,那么我们必须添加变量使得
他们不相互干扰。
题目的一般出法是父节点状态由子树状态决定.
所以,算法实现通常借助DFS过程。
Party at Hali-Bula


n个人形成一个关系树,每个节点代表一个人,
节点的根表示这个人的唯一的直接上司,只有根
没有上司。要求选取一部分人出来,使得每2个
人之间不能有直接的上下级的关系,求最多能选
多少个人出来,并且求出获得最大人数的选人方
案是否唯一。
这是一个经典的树型动态规划。
Party at Hali-Bula

简单的染色统计是不正确的
Party at Hali-Bula

人之间的关系形成树型结构

状态: 用dp[i][0]表示不选择i点时,i点及其子
树能选出的最多人数;dp[i][1]表示选择i点时,
i点及其子树的最多人数。
Party at Hali-Bula

状态转移方程:



对于叶子节点i, dp[i][0] = 0, dp[i][1] = 1
对于非叶子节点i,
dp[i][0] = ∑max(dp[j][0], dp[j][1]) (j是i的儿子)
dp[i][1] = 1 + ∑dp[j][0] (j是i的儿子)
最多人数即为max(dp[0][0], dp[0][1])
Party at Hali-Bula

如何判断最优解是否唯一?

新加一个状态dup[i][j],表示相应的dp[i][j]是否是
唯一方案。
对于叶子结点i, dup[i][0] = dup[i][1] = 1.
对于非叶子结点,




对于i的任一儿子j,若
(dp[j][0] > dp[j][1] && dup[j][0] == 0) ||
(dp[j][0] < dp[j][1] && dup[j][1] == 0) ||
(dp[j][0] == dp[j][1]),则dup[i][0] = 0
对于i的任一儿子j, 若dup[j][0] == 0, 则dup[i][1] = 0
Strategic game

一城堡的所有的道路形成一个n个节点的树,
如果在一个节点上放上一个士兵,那么和这个
节点相连的边就会被看守住,问把所有边看守
住最少需要放多少士兵。

典型的树型动态规划
Strategic game



dproot[ i ]表示以i为根的子树,在i上放置一个
士兵,看守住整个子树需要多少士兵。
all[ i ]表示看守住整个以i为根的子树需要多少
士兵。
状态转移方程:
叶子节点i:dproot[i] =1; all[i] = 0;
非叶子节点i:
dproot[i] = 1 + ∑all[j] (j是i的儿子);
all[i] = min( dproot[i], ∑dproot[j](j是i的儿子) );
Strategic game

这个题目还是比较简单的,如果把题目中看守
边变成看守相邻的点呢?留给你来思考^_^
状态压缩动态规划





状态压缩动态规划:
动态规划的状态有时候不容易表示出来,需要
用一些编码技术,把状态压缩的用简单的方式
表示出来。
典型方式:当需要表示一个集合有哪些元素时,
往往利用2进制用一个整数表示。
对于集合S={0,1, 2, …, N- 1} ,在N较小的情况下,S的子
集A可以用一个N位二进制数x 来表示.
表示方法:当i 属于A时,x 的第i 位为1;否则为0.
例如:当N=16时,集合A={2, 3, 5} ,可以表示为
x=00101100,即十进制整数44.
经典问题:TSP



一个n个点的带权的有向图,求一条路径,使
得这条路经过每个点恰好一次,并且路径上边
的权值和最小(或者最大)。
或者求一条具有这样性质的回路,这是经典的
TSP问题。
n <= 16 (重要条件,状态压缩的标志)
TSP



如何表示一个点集:
由于只有16个点,所以我们用一个整数表示一
个点集:
例如:
5 = 0000000000000101;(2进制表示)
它的第0位和第2位是1,就表示这个点集里有2
个点,分别是点0和点2。
31 = 0000000000011111; (2进制表示)
表示这个点集里有5个点,分别是0,1,2,3,
4;
TSP



所以一个整数i就表示了一个点集;
整数i可以表示一个点集,也可以表示是第i个
点。
状态:dp[i][j]表示经过点集i中的点恰好一次,
不经过其它的点,并且以j点为终点的路径,
权值和的最小值,如果这个状态不存在,就是
无穷大。
TSP

状态转移
单点集:状态存在dp[i][j] = 0;否则无穷大。
非单点集:
状态存在 dp[i][j] = min(dp[k][s] + w[s][j]), s∈k
k表示i集合中去掉了j点得到的集合,s是集合k
中的点并且dp[k][s]状态存在, 点s到点j有边
存在,w[s][j]表示边<s,j>的权值。
状态不存在,则dp[i][j]为无穷大。
TSP

最后的结果是:
min( dp[( 1<<n ) – 1][j] ) ( 0 <= j < n );

技巧:利用2进制,使得一个整数表示一个点集,
这样集合的操作可以用位运算来实现。
• 判断点j 是否属于集合i :i & (1<<j)
• 在集合i 中去除点j :i –(1<<j) 或者i & ( ~( 1 << j ) )
• 在集合i 中加入点j :i | (1<<j)
状态压缩DP:图的最长路
相关题目:sicily 1123. The Longest Walk
参考代码s1123.c
• 问题描述:给出一个带权有向图G=(V, E),
求图中的一条最长简单路径.
• 如果暴力穷举所有路径,复杂度不低于O(N!)
动态规划方程
• 状态表示:用ans[j][i]表示以j点为起点,并且经
过点集i中的点恰好一次而不经过其它点的路径长
度的最大值. 如果这个状态不存在,就是无穷小.
• 状态转移:
如果点集i 只包含一个点,ans[j][i] = 0 , ∀j;
否则,ans[j][i] = max(w[j][k] + ans[k][s])
s 表示i 集合中去掉了j点的集合,k 遍历集合s 中
的点,点j 到点k 有边存在,w[j][k]表示边(j, k) 的
权值.
• 最后结果便是所有ans[j][i]的最大值.
多阶段决策问题

跳舞机(ACM Regional Contest Shanghai
2000)
跳舞机

Moving one of his feet from the central point to any side
points will consume 2 units of his strength. Moving from
one side point to another adjacent side point will
consume 3 units, such as from the top point to the left
point. Moving from one side point to the opposite side
point will consume 4 units, such as from the top point to
the bottom point. Yet, if he stays on the same point and
tread again, he will use 1 unit.
跳舞机

状态:
d[i][j][k]为跳到第k个舞步时,左脚在位置i,右脚在位
置j时所需要耗费的最少体力

状态转移方程 :
d[i,j,k]=min{ d[s[k],j,k+1]+cost(i,s[k]),
d[i,s[k],k+1]+cost(j,s[k]) }
d[i,j,n]=min{ cost(i,s[n]), cost(j,s[n]) }
最优值为 d[i,j,1]
Longest Common Substring
Longest Common Substring

状态:
记Xi = (x1,x2,…,xi), Yi = (y1,y2,…,yi)
c[i][j]记录序列Xi和Yj的最长公共子序列的长度。

状态转移方程 :
c(i, j) = 0
c(i, j) = c(i-1, j-1) + 1
c(i, j) = max{c(i,j-1), c(i-1,j)}
if i=0 or j=0
if i, j>0 and xi = yj
if i, j>0 and xi ≠ yj
Edit Distance



When a spell checker encounters a possible misspelling, it looks in
its dictionary for other words that are close by. What is the
appropriate notion of closeness in this case?
A natural measure of the distance between two strings is the extent
to which they can be aligned, or matched up. Technically, an
alignment is simply a way of writing the strings one above the other.
For instance, here are two possible alignments of SNOWY and
SUNNY:
S–NOWY
–SNOW–Y
SUNN– Y
SUN– –NY
Cost: 3
Cost: 5
The ' – ' indicates a 'gap'; any number of these can be placed in
either string. The cost of an alignment is the number of columns in
which the letters differ. And the edit distance between two strings is
the cost of their best possible alignment.
Edit Distance


Our goal is to find the edit distance between two strings
x[1…m] and y[1…n]. What is a good subproblem?
How about looking at the edit distance between some
prefix x of the first string, x[1…i], and some prefix of the
second, y[1…j]? Call this subproblem E(i, j). Our goal
objective, then, is to compute E(m, n).
Edit Distance



For this to work, we need to somehow express E(i, j) in
terms of smaller subproblems.
Let's see—
what do we know about the best alignment
between x[1…i] and y[1…j]? Well, its rightmost column
can only be one of three things:
E(i, j) = min{1 + E(i-1, j), 1 + E(i, j-1), diff(i, j) + E(i-1, j-1)}
where diff(i, j) is defined to be 0 if x[i] = y[j] and 1
otherwise.
Edit Distance

For instance, in computing the edit distance between
EXPONENTIAL and POLYNOMIAL, subproblem E(4, 3)
corresponds to the prefixes EXPO and POL. The
rightmost column of their best alignment must be one of
the following:
Thus, E(4, 3) = min{1 + E(3, 3), 1 + E(4, 2), 1 + E(3, 2)}.
Edit Distance

The answers to all the subproblems E(i, j) form a twodimensional table.
Edit Distance

The algorithm for edit distance:

The overall running time is just the size of the table,
O(mn).
总结

实质:


两种实现方式:



重叠子问题,最优子结构,记忆化求解,空间换时间
自底向上(bottom up)
自顶向下(top down)
基本概念




状态
阶段
状态转移方程
决策

问题分类:






零维状态存储问题
一维状态存储问题
二维状态存储问题
树状DP
状态压缩DP
多阶段决策问题
sicily上的一些dp题:
sicily1010 Zipper
sicily1011 Lenny's Lucky Lotto
sicily1019 Apple Tree (树型dp)
sicily1057 Rhyme Schemes
sicily1073 Pearls
sicily1123 The Longest Walk(状态压缩dp)
sicily1148 过河(路径压缩,贪心动态规划)
sicily1158 Pick numbers
sicily1222 单词选择(这题的hash很恶心)
sicily1264 Atomic Car Race (基本dp题)
sicily1404 Hie with the Pie(状态压缩dp)
sicily1687 Permutation
sicily1822 Fight Club
sicily1828 Minimal

similar documents