1、钢条切割
自顶向下
int maXt(int a, int b)
{
if (a>b)
{
return a;
}
return b;
}
int cutRod(vector<int>vec, int n)
{
if (n==0)
{
return 0;
}
/*if (memoziedArray[n-1]>0)
{
return memoziedArray[n - 1];
}*/
int max = 0;
for (int i = 1; i <= n&&i <= vec.size();i++)
{
max = maXt(max, vec[i - 1] + cutRod(vec, n - i));
}
return max;
}
在测试时,我们发现,当钢条的长度稍微变大(比如n=30)时,上述程序的运行时间会大大增大。仔细考虑原因,会发现实际上我们做了很多重复的递归操作。比如在求解cutRod(p, n)
过程中,我们会递归求解cutRod(p, 0 ~ n-1)
,而在求解cutRod(p, n-1)
的过程中,同样我们会递归求解cutRod(p, 1 ~ n-1)
,可以看出,仅仅就是这两次的调用,就重复调用了n-2次。时间效率当然会下降。
改进:
int maXt(int a, int b)
{
if (a>b)
{
return a;
}
return b;
}
int *memoziedArray;
int cutRod(vector<int>vec, int n)
{
memoziedArray = memoziedArray == NULL ? new int[n] : memoziedArray;
if (n==0)
{
return 0;
}
if (memoziedArray[n-1]>0)
{
return memoziedArray[n - 1];
}
int max = 0;
for (int i = 1; i <= n&&i <= vec.size();i++)
{
max = maXt(max, vec[i - 1] + cutRod(vec, n - i));
}
memoziedArray[n - 1] = max;
return max;
}
自底向上方法
int bottomUpCutRod(vector<int>vec, int n)
{
int *memoziedArray = new int[n + 1];
memoziedArray[0] = 0;
int max = 0;
for (int i = 1; i <= n;i++)
{
for (int j = 1; j <= i&&j <= vec.size();j++)
{
max = maXt(max, vec[j - 1] + memoziedArray[i-j]);
}
memoziedArray[i] = max;
}
return max;
}
2、最长公共子串问题LCS
设序列X={x1,x2,……xm}和Y={y1,y2,……yn}的最长公共子序列为Z={z1,z2,……zk}。则有:
(1)若xm=yn,则zk=xm=yn,且zk-1是Xm-1和Yn-1的最长公共子序列。
(2)若xm!=yn且zk!=xm,则Z是Xm-1和Y的最长公共子序列。
(3)若xm!=yn且zk!=yn,则Z是X和Yn-1的最长公共子序列。
其中,Xm-1={x1,x2……xm-1},Yn-1={y1,y2……yn-1},Zk-1={z1,z2……zk-1}。
递推关系:用c[i][j]记录序列Xi和Yj的最长公共子序列的长度。其中,Xi={x1,x2……xi},Yj={y1,y2……yj}。当i=0或j=0时,空序列是xi和yj的最长公共子序列。此时,c[i][j]=0;当i,j>0,xi=yj时,c[i][j]=c[i-1][j-1]+1;当i,j>0,xi!=yj时,c[i][j]=max{c[i][j-1],c[i-1][j]}。
求最长公共子串的长度
void LCSLength(int m, int n, char *x, char *y, int **c, int **b)
{
int i, j;
for (i = 1; i <= m; i++)
c[i][0] = 0;
for (i = 1; i <= n; i++)
c[0][i] = 0;
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
if (x[i] == y[j])
{
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = 1;
}
else if (c[i - 1][j] >= c[i][j - 1])
{
c[i][j] = c[i - 1][j];
b[i][j] = 2;
}
else
{
c[i][j] = c[i][j - 1];
b[i][j] = 3;
}
}
}
}
求公共子串
void LCS(int i, int j, char *x, int **b)
{
if (i == 0 || j == 0)
{
return;
}
if (b[i][j] == 1)
{
LCS(i - 1, j - 1, x, b);
cout << x[i] << " ";
}
else if (b[i][j] == 2)
{
LCS(i - 1, j, x, b);
}
else
{
LCS(i, j - 1, x, b);
}
}
3、LSC改进:将数组b省去
void LCSLength(int m,int n,char *x,char *y,int **c)
{
int i,j;
for(i=1; i<=m; i++)
c[i][0] = 0;
for(i=1; i<=n; i++)
c[0][i] = 0;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
{
if(x[i]==y[j])
{
c[i][j]=c[i-1][j-1]+1;
}
else if(c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
}
else
{
c[i][j]=c[i][j-1];
}
}
}
}
void LCS(int i,int j,char *x,int **c)
{
if(i==0 || j==0)
{
return;
}
if(c[i][j]==c[i-1][j-1]+1)
{
LCS(i-1,j-1,x,c);
cout<<x[i]<<" ";
}
else if(c[i-1][j]>=c[i][j-1])
{
LCS(i-1,j,x,c);
}
else
{
LCS(i,j-1,x,c);
}
}
4、菲波那切数列
//避免重复运算的斐波那契数列运算
int fib(int n, map<int, int>my_Map)
{
if (n==0||n==1)
{
return 1;
}
else
{
map<int, int>::iterator iter = my_Map.find(n);
if (iter==my_Map.end())
{
int temp = fib(n - 1, my_Map) + fib(n - 2, my_Map);
my_Map.insert(pair<int, int>(n, temp));
return temp;
}
else
{
return iter->second;
}
}
}
5、连续最长公共子串(多个)
string getLCSLength(string &s, string &t)
{
int p = s.length();
int q = t.length();
string **num = new string *[p];
for(int i=0;i<p;i++)
{
num[i] = new string[q];
}
char char1 = '\0';
char char2 = '\0';
int len = 0;
string lcs = "" ;
for(int i=0; i<p; i++)
{
for (int j=0; j<t.length(); j++)
{
char1 = s.at(i);
char2 = t.at(j);
if(char1!=char2)
{
num[i][j] = "" ;
}
else
{
if (i==0||j==0)
{
num[i][j]=char1;
}
else
{
num[i][j]=num[i-1][j-1]+char1;
}
if(num[i][j].length()>len)
{
len = num[i][j].length() ;
lcs = num[i][j];
}
else if(num[i][j].length()==len)
{
lcs = lcs + "," + num[i][j];
}
}
}
}
for(int i=0;i<p;i++)
{
delete[] num[i];
}
delete[] num;
return lcs;
}
6、最大子段问题
问题定义:对于给定序列a1,a2,a3……an,寻找它的某个连续子段,使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。
int MaxSum(int n,int *a,int& besti,int& bestj)
{
int sum = 0;
for(int i=0; i<n; i++)//控制求和起始项
{
for(int j=i; j<n; j++)//控制求和结束项
{
int thissum = 0;
for(int k=i; k<=j; k++)//求和
{
thissum += a[k];
}
if(thissum>sum)//求最大子段和
{
sum = thissum;
besti = i;
bestj = j;
}
}
}
return sum;
}
7、最大子段问题,改进
int MaxSum(int n,int *a,int& besti,int& bestj)
{
int sum = 0;
for(int i=0; i<n; i++)//控制求和起始项
{
int thissum = 0;
for(int j=i; j<=n; j++)//控制求和结束项
{
thissum += a[j];//求和
if(thissum>sum)
{
sum = thissum;
besti = i;
bestj = j;
}
}
}
return sum;
}
8、最大子段和:分治算法
int MaxSubSum(int *a,int left,int right)
{
int sum = 0;
if(left == right)
{
sum = a[left]>0?a[left]:0;
}
else
{
int center = (left+right)/2;
int leftsum = MaxSubSum(a,left,center);
int rightsum = MaxSubSum(a,center+1,right);
int s1 = 0;
int lefts = 0;
for(int i=center; i>=left;i--)
{
lefts += a[i];
if(lefts>s1)
{
s1=lefts;
}
}
int s2 = 0;
int rights = 0;
for(int i=center+1; i<=right;i++)
{
rights += a[i];
if(rights>s2)
{
s2=rights;
}
}
sum = s1+s2;
if(sum<leftsum)
{
sum = leftsum;
}
if(sum<rightsum)
{
sum = rightsum;
}
}
return sum;
}
int MaxSum(int n,int *a)
{
return MaxSubSum(a,0,n-1);
}
9、最大子段和:动态规划
int MaxSum(int n,int *a)
{
int sum=0,b=0;
for(int i=1; i<=n; i++)
{
if(b>0)
{
b+=a[i];
}
else
{
b=a[i];
}
if(b>sum)
{
sum = b;
}
}
return sum;
}
10、最大矩阵和
int MaxSum2(int m,int n,int a[M][N])
{
int sum = 0;
int *b = new int[n+1];
for(int i=0; i<m; i++)//枚举行
{
for(int k=0; k<n;k++)
{
b[k]=0;
}
for(int j=i;j<m;j++)//枚举初始行i,结束行j
{
for(int k=0; k<n; k++)
{
b[k] += a[j][k];//b[k]为纵向列之和
int max = MaxSum(n,b);
if(max>sum)
{
sum = max;
}
}
}
}
return sum;
}
int MaxSum(int n,int *a)
{
int sum=0,b=0;
for(int i=1; i<=n; i++)
{
if(b>0)
{
b+=a[i];
}
else
{
b=a[i];
}
if(b>sum)
{
sum = b;
}
}
return sum;
}
11、最大m子段和
问题描述:给定由n个整数(可能为负数)组成的序列a1,a2,a3……an,以及一个正整数m,要求确定此序列的m个不相交子段的总和达到最大。最大子段和问题是最大m字段和问题当m=1时的特殊情形。
int MaxSum(int m,int n,int *a)
{
if(n<m || m<1)
return 0;
int *b = new int[n+1];
int *c = new int[n+1];
b[0] = 0;//b数组记录第i行的最大i子段和
c[1] = 0;//c数组记录第i-1行的最大i-1子段和
for(int i=1; i<=m; i++)
{
b[i] = b[i-1] + a[i];
c[i-1] = b[i];
int max = b[i];
//n-m+i限制避免多余运算,当i=m时,j最大为n,可据此递推所有情形
for(int j=i+1; j<=i+n-m;j++)
{
b[j] = b[j-1]>c[j-1]?b[j-1]+a[j]:c[j-1]+a[j];
c[j-1] = max;//预先保存第j-1行的最大j-1子段和
if(max<b[j])
{
max = b[j];
}
}
c[i+n-m] = max;
}
int sum = 0;
for(int j=m; j<=n; j++)
{
if(sum<b[j])
{
sum = b[j];
}
}
return sum;
}
12、背包问题
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?