一、最长公共子序列:
1.算法核心:
(1)最优子结构:
a.若,则
,且
是
与
的最长公共子序列;
b.若,当
时,
是
与
的最长公共子序列,
当 时,
是
与
的最长公共子序列。
(2)递归关系:c[i][j] 记录序列 Xi 与 Yi 的最长公共子序列
2.代码实现:
#include<iostream>
#include<string.h>
using namespace std;
int LCSlength(string A,string B,int m,int n,int **b)
{
int **LCS=new int *[m+1];
/*
1、初始化LCS[i][j];
2、空串与任意串的公共子序列长度为 0 (c[i][0]和c[0][j])
*/
for(int i=0;i<=m;i++)
{
LCS[i]=new int [n+1];
for(int j=0;j<=n;j++)
{
LCS[i][j]=0;
}
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(A[i]==B[j]) //c[i][j]=c[i-1][j-1]+1
{
LCS[i][j]=LCS[i-1][j-1]+1;
b[i][j]=1; //c[i][j]的结果由第 1个子问题得到
}
else //c[i][j]=max{c[i][j-1],c[i-1][j]}
{
if(LCS[i][j-1]>LCS[i-1][j])
{
LCS[i][j]=LCS[i][j-1];
b[i][j]=2;//c[i][j]的结果由第 2个子问题得到
}
else
{
LCS[i][j]=LCS[i-1][j];
b[i][j]=3; //c[i][j]的结果由第 3个子问题得到
}
}
}
}
return LCS[m][n];
}
void LCS(int **b,string A,int i,int j)
{
if(i==0||j==0)
return;
if(b[i][j]==1)
{
LCS(b,A,i-1,j-1);
cout<<A[i]<<' ';
}
else if(b[i][j]==2)
{
LCS(b,A,i,j-1);
}
else
{
LCS(b,A,i-1,j);
}
}
int main()
{
string A = "BCBACABBACDX";
string B = "BCXYDADJL"; // A 和 B的LCS为BCAD
int m = A.length();
int n = B.length();
int length;
int **b=new int *[m+1];
for(int i=0;i<=m;i++)
{
b[i]=new int [n+1];
for(int j=0;j<=n;j++)
{
b[i][j]=0;
}
}
A = ' ' + A;
B = ' ' + B;
cout<<"序列A:"<<A<<endl;
cout<<"序列B:"<<B<<endl;
length=LCSlength(A,B,m,n,b);
cout<<"A和B的最长公共子序列长度为:"<<length<<endl;
cout<<"A和B的最长公共子序列为:";
LCS(b,A,m,n);
return 0;
}
/*
序列A: BCBACABBACDX
序列B: BCXYDADJL
A和B的最长公共子序列长度为:4
A和B的最长公共子序列为:B C A D
*/
二、0-1背包问题:
1.算法核心:
(1)对其子问题:
其最优解为,背包重量为 j,可选择的物品为i,i+1,... ,n
(2)递归式:
2.代码实现:
#include <iostream>
#include<ctime>
using namespace std;
#include<string.h>
#define NUM 50
#define CAP 1500
int v[NUM];//价值
int w[NUM];//重量
int p[NUM][CAP];//记录矩阵
void knapsack(int c, int n) //容量 c 可选物品 n
{
//初始化m[n][j]
int jMax=min(w[n]-1,c);
for( int j=0; j<=jMax; j++) //0<=j<w[n]
p[n][j]=0;
for( int j=w[n]; j<=c; j++) //j>=w[n]
p[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++) //0<=j<w[i]
p[i][j]=p[i+1][j];
for(int j=w[i]; j<=c; j++) //j>=w[i]
p[i][j]=max(p[i+1][j], p[i+1][j-w[i]]+v[i]);
}
/*添加最后一份物品
1.若0<=c<w[1]:p[1][c]=p[2][c]
2.若c>=w[1]: 取m(2,c)与m(2,c-w[1])+v[1]二者中的最大值
*/
p[1][c]=p[2][c];
if (c>=w[1])
p[1][c]=max(p[1][c], p[2][c-w[1]]+v[1]);
}
void traceback( int c, int n, int x[ ])
{
for(int i=1; i<n; i++)
{
if (p[i][c]==p[i+1][c]) //未放入物品 i
{
x[i]=0;
}
else //放入了物品 i
{
x[i]=1; c-=w[i];
}
}
x[n]=(p[n][c])? 1:0; //最初考虑的那个物品是否为0 若为0则说明未放入且其W>C
}
int main ()
{
int x[NUM];
int W;
int n;
printf("输入背包总容量M:");
while (scanf("%d", &W) && W)
{
printf("输入要装入的物品n:");
scanf("%d", &n);
printf("依次输入物品的重量w及价值v:\n");
for (int i=1; i<=n; i++)
scanf("%d%d", &w[i], &v[i]);
memset (p, 0, sizeof(p));//数组初始化
knapsack(W, n);//进行画表
printf("最优值为:%d\n", p[1][W]);
traceback(W, n, x);//根据表和价值情况 获取物品选择路线
printf("最优解为:\n");
for (int i=1; i<=n; i++)
if (x[i]) printf("%d ", i);
printf("\n");
}
return 0;
}
/*输入背包总容量M:10
输入要装入的物品n:5
依次输入物品的重量w及价值v:
2 6
2 3
6 5
5 4
4 6
最优值为:15
最优解为:
1 2 5
*/
三、流水作业调度:
1.算法核心:
根据Johnson法则,我们可以对作业进行排序和分类:
(1)尽可能早的安排 的作业,并按
增序排列 ;
(2)对于 的作业,按
降序排列;
2.代码实现:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define N 100
struct work
{
int key;//记录值
int index;//记录序号
int judge;//记录b[i]是否大于a[i] ,大的话为1
}d[N];
int main()
{
int i,j,k,n,a[N],b[N],c[N];
cout<<"请输入作业数:"<<endl;
cin>>n;
cout<<"请输入在第一台机器上加工的时间:"<<endl;
for(i=1;i<=n;i++)
{
cin>>a[i];
}
cout<<"请输入在第二台机器上加工的时间:"<<endl;
for(i=1;i<=n;i++)
{
cin>>b[i];
}
for(i=1;i<=n;i++)//对结构数组赋初值,d[i]储存两个机器加工中较小的值
{
if(a[i]>b[i])//记录第 2 类数据
{
d[i].key=b[i];
d[i].judge=2;
}
else //记录第 1 类数据
{
d[i].key=a[i];
d[i].judge=1;
}
d[i].index=i;
}
for(i=1;i<=n;i++)//对结构数组赋初值,d[i]储存两个机器加工中较小的值
{
if(a[i]>b[i])//记录第 2 类数据
{
d[i].key=b[i];
d[i].judge=2;
}
else//记录第 1 类数据
{
d[i].key=a[i];
d[i].judge=1;
}
d[i].index=i;
}
for(i=1;i<n;i++)//对值进行增序排序
{
for(j=1;j<=n-i;j++)
{
if(d[j].key>d[j+1].key)//将较大值交换到靠后的位置
{
int index,key,judge;
index=d[j].index;
key=d[j].key;
judge=d[j].judge;
d[j].index=d[j+1].index;
d[j].key=d[j+1].key;
d[j].judge=d[j+1].judge;
d[j+1].index=index;
d[j+1].key=key;
d[j+1].judge=judge;
}
}
}
for(i=1,k=1,j=n;i<=n;i++)//c[i]即为最终的作业执行顺序
{
if(d[i].judge==1)
c[k++]=d[i].index;//将a[i]<b[i]的排前面
else
c[j--]=d[i].index;//正好将b[i]按降序排序
}
i=a[c[1]]; //初始化,M1最优调度序列下的作业结束时间
j=i+b[c[1]]; //初始化,M2最优调度序列下的作业结束时间
for(k=2;k<=n;k++) //从第二个开始
{
i+=a[c[k]]; //本作业在M1上的结束时间
if(j>i) //此时上一个作业在 M2上还没加工完,那么作业堆积
{
j=j+b[c[k]];//本作业在 M2上的结束时间就是上一个作业在 M2上的结束时间加上本作业在M2上的运行时间
}
else
{
j=i+b[c[k]];//M2正好空着,那就在M1上的结束时间加上M2上的运行时间
}
}
printf("最短时间为%d\n作业顺序为:",j);
for(i=1;i<=n;i++)
{
printf("%d ",c[i]);
}
return 0;
}
/*
请输入作业数:
6
请输入在第一台机器上加工的时间:
30 120 50 20 90 110
请输入在第二台机器上加工的时间:
80 100 90 60 30 10
最短时间为430
作业顺序为:4 1 3 2 5 6
*/
四、最优二叉搜索树:
1.算法核心:
(1)w[i,j] 概率权重矩阵:
(2)e[i,j] 最优二叉搜索树的搜索代价矩阵:
(3)root[i,j] 记录使得e[i,j] 取得最小值时的 r 值
2.代码实现:
#include <iostream>
using namespace std;
const int MaxVal = 9999;
const int n = 5;
//搜索到根节点和虚拟键的概率
double p[n + 1] = {-1,0.15,0.1,0.05,0.1,0.2};
double q[n + 1] = {0.05,0.1,0.05,0.05,0.05,0.1};
int root[n + 1][n + 1];//记录根节点
double w[n + 2][n + 2];//子树概率总和
double e[n + 2][n + 2];//子树期望代价
void optimalBST(double *p,double *q,int n)
{
//初始化只包括虚拟键的子树
for (int i = 1;i <= n + 1;++i)
{
w[i][i - 1] = q[i - 1];
e[i][i - 1] = q[i - 1];
}
//由下到上,由左到右逐步计算
for (int len = 1;len <= n;++len)
{
for (int i = 1;i <= n - len + 1;++i)
{
int j = i + len - 1;
e[i][j] = MaxVal;
w[i][j] = w[i][j - 1] + p[j] + q[j];//更新w[i][j]的值
for (int k = i;k <= j;++k)//求最小的 temp值及其对应的根
{
double temp = e[i][k - 1] + e[k + 1][j] + w[i][j];
if (temp < e[i][j])
{
e[i][j] = temp;
root[i][j] = k;
// cout << root[i][j] << " ";
}
}
}
}
cout << "各子树的根:" << endl;
for (int i = 1;i <= n;++i)
{
for (int j = 1;j <= n;++j)
{
cout << root[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
//输出最优二叉查找树所有子树的根
void printRoot()
{
}
//打印最优二叉查找树的结构
//打印出[i,j]子树,它是根r的左子树和右子树
void printOptimalBST(int i,int j,int r)
{
int rootChild = root[i][j];//子树根节点
if (rootChild == root[1][n])
{
//输出整棵树的根
cout << "k" << rootChild << "是根" << endl;
printOptimalBST(i,rootChild - 1,rootChild);
printOptimalBST(rootChild + 1,j,rootChild);
return;
}
if (j < i - 1)
{
return;
}
else if (j == i - 1)//遇到虚拟键
{
if (j < r)
{
cout << "d" << j << "是" << "k" << r << "的左孩子" << endl;
}
else
cout << "d" << j << "是" << "k" << r << "的右孩子" << endl;
return;
}
else//遇到内部结点
{
if (rootChild < r)
{
cout << "k" << rootChild << "是" << "k" << r << "的左孩子" << endl;
}
else
cout << "k" << rootChild << "是" << "k" << r << "的右孩子" << endl;
}
printOptimalBST(i,rootChild - 1,rootChild);
printOptimalBST(rootChild + 1,j,rootChild);
}
int main()
{
optimalBST(p,q,n);
//printRoot();
cout << "最优二叉树结构:" << endl;
printOptimalBST(1,n,-1);
}
/*
各子树的根:
1 1 2 2 2
0 2 2 2 4
0 0 3 4 5
0 0 0 4 5
0 0 0 0 5
最优二叉树结构:
k2是根
k1是k2的左孩子
d0是k1的左孩子
d1是k1的右孩子
k5是k2的右孩子
k4是k5的左孩子
k3是k4的左孩子
d2是k3的左孩子
d3是k3的右孩子
d4是k4的右孩子
d5是k5的右孩子
*/