(1)0-1背包问题
问题描述:
0-1背包问题的描述:在n种物品中选择1个或0个第i种物品,装入背包容量为m的背包,使得背包价值达到最大。
思路与关键点:
用到了max函数,用于返回两个数之中的较大值,包含在头文件<algorithm>中。下面的代码用头文<bits/stdc++.h>,包含几乎所有常用头文件。其中,当然包含<algorithm>。思路:对于每一个物品,有两种选择,要么放,要么不放。可以把问题分解成从背包容量为j<m的问题,无论j怎么变,我们都取其中的最优解,直到j=m时,就可以得到问题的最优解了。
下面的代码内部循环一边输入第i个物品的价值和重量,一边给背包容量为j的背包更新对应的背包最大价值,从m递减到放不下第i个物品时,第i轮更新结束。当外部循环从1递增到n时,就得到了n种物品放入背包容量为m的问题的最优解。
代码:
#include<bits/stdc++.h>using namespace std;const int MAXN = 1005;
int f[MAXN]; // 建立一个长度为MAX的数组,每个元素都为0int main()
{int n, m; printf("请输入物品的个数和背包的容量:"); cin >> n >> m;
printf("\n物品的数量是:%d,背包的容量是:%d\n",n,m); for(int i = 1; i <= n; i++) {printf("请输入第%d个物品的价值和重量\n",i);int v, w;//v是物品的价值,w是物品的重量 cin >> v >> w; // 边输入边处理printf("第%d个物品的价值是:%d,重量是:%d\n",i,v,w); for(int j = m; j >= w; j--)f[j] = max(f[j], f[j - w] + v);//判断是否选择第j个物品 }//输出背包容量为m的最大价值
printf("背包的最大价值为:%d\n",f[m]);return 0;
}
运行结果:
时间复杂度与空间复杂度分析:
时间复杂度为主要是两个for循环为0(),空间复杂度主要为变量为O(1)
(2)最长公共子序列问题
问题描述:
给定任意两个字符串,求求按下标严格递增的最长子序列长。例如:ABC和ADB这两个最长公共子序列为AB。意思就是说可以跳着取字符,但字符在原字符串的顺序保持不变。
思路与关键点:
Strlen()函数用来求字符数组的长度的,在头文件<string.h>中。思路:对于求长度为n的字符串a和长度为m的字符串b,我们有两种选择。第一种,可以看作 子问题求a去掉最后一个字符得到的子串和b进行比较,找到最大公共子串长。第二种,可以看作 子问题求b去掉最后一个字符得到的子串和a进行比较,找到最大公共子串长。去掉的字符有可能是a和b的共同字符,这时记录的最长公共字符串长度要加一。
下面代码中,数组f[i][j]记录了字符串a的前i个字符和字符串b的前j个字符的最长公共子串长。通过两层for循环,遍历由字符串a和b组成的二维表,每次减少一个不同的字符相当于缩小一行或一列。每次减少一个相同的字符,相当于,减少一行和一列;我们每次减少一个字符时,都进行上面讨论的两种选择比较,找到二者的最大值。
代码:
#include<stdio.h>
#include<string.h>int max(int a,int b)
{return a>b?a:b;
}
int main()
{int i,j,k; char a[600];char b[600];int f[600][600];//f[i][j]表示a字符串的前i个字符和b字符串的前j个字符的最长子序列长度 printf("请输入字符串a和b\n"); while(scanf("%s %s",a,b)!=EOF)//不停地输入字符串,判断最长公共子串长 {int n=strlen(a);//计算字符串a的长度 int m=strlen(b);/*当任意一个字符串为空时,它们的公共子串长为0*/ for(i=0;i<=n;i++){f[i][0]=0;}for(i=0;i<=m;i++){f[0][i]=0;}/*不断缩小搜索范围。*/ for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(a[i-1]==b[j-1]){f[i][j]=f[i-1][j-1]+1;}else{f[i][j]=max(f[i-1][j],f[i][j-1]);}}}printf("最长公共子序列长度为:\n"); printf("%d\n",f[n][m]);printf("请输入字符串a和b\n"); }return 0;
}
运行结果:
时间复杂度与空间复杂度分析:
时间复杂度为主要是两个for循环为0(),空间复杂度主要为二维数组为0()
(3)最大子段和问题
问题描述:
思路与关键点:
我们要求连续的最大子段和,实际就是尽量找正数,跳过负数。所选的第一个数必然是正数。在下面的代码中,设置了一个临时变量b,用来存放当前的最大子段和。从0开始,遍历数组,b的初值为0,进入循环。b的值变为数组的第一个元素,如果第一个元素是正数,那么,更新sum的值为第一个元素的大小。如果是负数,就接着往后找,直到找到一个正数,开始计算后面的连续几个数的和,看看是否能够比当前的最大和sum大。如果比sum小,就不必更新sum。如果后面的数忽然出现负数,那么,一定会越加越小,最后小于0,就会从后面找到的第一个正数开始重新找连续的最大子段和。
代码:
#include<stdio.h>
int Maxsum(int n,int *a){int sum=0,b=0;for(int i=0;i<n;i++){if(b>0){b+=a[i];}else{b=a[i];}if(b>sum){sum=b;}}return sum;
}
int main(){int a[10]={1,2,6,9,0,-1,3,-2,9,8};int len=sizeof(a)/sizeof(a[0]);
int Max=Maxsum(len,a);
printf("最大子段和为:%d\n",Max);
return 0;
}
运行结果:
时间复杂度与空间复杂度分析:
时间复杂度为0(n),空间复杂度主要为一维数组0(n)