1.问题
Lcs:
给定序列X=<x1,x2,…,xm>,Y=<y1,y2,…,yj>,求X和Y的最长公共子序列。
背包问题:
有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
2.解析
Lcs:
Xi=<x1,x2,…,xi>
Yj=<y1,y2,…,yj>
Zk=<z1,z2,…,zk>
如果Zk是Xi和Yj的最长公共子序列
(1)xi = yj,那么zk = xi = yj,Zk-1是Xi-1和Yj-1的最长公共子序列
(2)xi ≠ yj,那么zk ≠ xi,Zk-1是Xi-1和Yj的最长公共子序列
(3)xi ≠ yj,那么zk ≠ yi,Zk-1是Xi和Yj-1的最长公共子序列
举例:
X=<A, B, C, B>
Y=<B, A, C>
m=0-4
n=0-3
1.i=1
a) j=1 X.A<>Y.B: C[1,1]=max(C[1,0], C[0,1])= max(0, 0)=0, 删除 y
b) j=2 X.A==Y.A: C[1,2]= C[0,1]+1 =1, 删除两个
c) j=3 X.A<>Y.C: C[1,3]=max(C[1,2], C[0,3])= max(1, 0)=1, 删除 y
2.i=2
a) j=1 X.B==Y.B: C[2,1] = C[1,0]+1 =1, 删除两个
b) j=2 X.B<>Y.A: C[2,2]= max(C[2,1], C[1,2])= max(1, 0)=1, 删除 y
c) j=3 X.B<>Y.C: C[2,3]=max(C[2,2], C[1,3])= max(1, 1)=1, 删除 y
3.i=3
a) j=1 X.C<>Y.B: C[3,1]=max(C[3,0], C[2,1])= max(0, 1)=1, 删除 x
b) j=2 X.C<>Y.A: C[3,2]= max(C[3,1], C[2,2])= max(1, 1)=1, 删除 y
c) j=3 X.C==Y.C: C[3,3]= C[2,2]+1 =2, 删除两个
4.i=4
a) j=1 X.D<>Y.B: C[4,1]=max(C[4,0], C[3,1])= max(0, 1)=1, 删除 x
b) j=2 X.D<>Y.A: C[4,2]=max(C[4,1], C[3,2])= max(1, 1)=1, 删除 y
c) j=3 X.D<>Y.C: C[4,3]=max(C[4,2], C[3,3])= max(1, 2)=2, 删除 x
根据上面四条数据建立表。
再依据三条规则,最后得到结果<B , C>
背包算法:
当前Fk-1(y1)最大价值:后续两种策略
- 继续装 k-1 号物品
- 装 k 号物品
Fk(y)表示只允许装前 k 种物品,背包总重不超过 y(重量)时背包的最大价值
1.不装第 k 种物品: Fk-1(y)
2.至少装 1 件 k 中物品:仅仅装一件物品 k,排除一件物品 k 后(减去重量,加上其价值)递归(可能再减一件物品 k,或者 k-1 物品),物品 k 的重量和价值分别为 wk,vk;Fk(y-wk)表示在(y-wk)重量中,(已排
除一件 k 物品的前提下)只允许装前 k 种物品时的最大价值。
3.F0(y)表示背包不装物品的价值
4.Fk(y)背包重量限制为 0 时的最大价值
5.F1(y)只装第一种物品,[y/w1]个数
6.Fk(y)=-∞用于比较
举例:
体积:2 3 4 5
重量:3 4 5 6
- 填表,首先初始化边界条件,V(0,j)=V(i,0)=0
- 如,i=1,j=1,w(1)=2,v(1)=3,有j<w(1),故V(1,1)=V(1-1,1)=0;又如i=1,j=2,w(1)=2,v(1)=3,有j=w(1),故V(1,2)=max{V(1-1,2),V(1-1,2-w(1))+v(1) }=max{0,0+3}=3;如此下去,填到最后一个,i=4,j=8,w(4)=5,v(4)=6,有j>w(4),故V(4,8)=max{V(4-1,8),V(4-1,8-w(4))+v(4) }=max{9,4+6}=10
- 最后的得出的结果Vmax=V(4,8)=10
3.设计
Lcs:
void z(int B[][130], int i, int j) {
if (i == 0 || j == 0) {//长度为0跳出
return;
}
if (B[i][j] == 2) {
z(B, i - 1, j - 1);
printf("%c ", X[i]);//输出符合的字母
}
else if (B[i][j] == 1) {
z(B, i - 1, j);
}
else {
z(B, i, j - 1);
}
}
void LCS(char X[], int m, char Y[], int n, int C[][130], int B[][130]) {
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; ++j) {
if (X[i] == Y[j]) {//第一种情况,这时Zk-1是Xi-1和Yj-1的最长公共子序列
C[i][j] = C[i - 1][j - 1] + 1;
B[i][j] = 2;
}
else
{
if (C[i - 1][j] > C[i][j - 1])//第二种情况,这时Zk-1是Xi-1和Yj的最长公共子序列
{
C[i][j] = C[i - 1][j];
B[i][j] = 1;
}
else {
C[i][j] = C[i][j - 1];//第三种情况,这时Zk-1是Xi和Yj-1的最长公共子序列
B[i][j] = 0;
}
}
}
}
z(B, m, n);
}
背包问题:
void Knapsack(int v[], int w[], int c, int n, int m[][10])
{
int jMax = min(w[n] - 1, c);//背包剩余容量上限 范围[0~w[n]-1]
for (int j = 0; j <= jMax; j++)
{
m[n][j] = 0;
}
for (int j = w[n]; j <= c; j++)//限制范围[w[n]~c]
{
m[n][j] = v[n];
}
for (int i = n - 1; i > 1; i--)
{
jMax = min(w[i] - 1, c);
for (int j = 0; j <= jMax; j++)//背包不同剩余容量j<=jMax<c
{
m[i][j] = m[i + 1][j];//没产生任何效益
}
for (int j = w[i]; j <= c; j++) //背包不同剩余容量j-wi >c
{
m[i][j] = max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);
}
}
m[1][c] = m[2][c];
if (c >= w[1])
{
m[1][c] = max(m[1][c], m[2][c - w[1]] + v[1]);
}
}
//x[]数组存储对应物品0-1向量,0不装入背包,1表示装入背包
void Traceback(int m[][10], int w[], int c, int n, int x[])
{
for (int i = 1; i < n; i++)
{
if (m[i][c] == m[i + 1][c])
{
x[i] = 0;
}
else
{
x[i] = 1;
c -= w[i];
}
}
x[n] = (m[n][c]) ? 1 : 0;
}
4.分析
Lcs的时间复杂度为O(mn).
背包问题的时间复杂度为O(n^2)