算法第三章作业
1.对动态规划算法的理解
动态规划算法与分治法类似,其基本思想是将待求解问题分解成若干子问题,先求解子问题,再结合这些子问题的解得到原问题的解。与分治法不同的是,适合用动态规划法求解的问题经分解得到的子问题往往不是互相独立的。若用分治法来解,那么分解得到的子问题数目往往是指数级的,有些子问题被重复计算了许多次。所以可以记录下已经算得的子问题的答案,需要时直接调用即可,不需要重复计算。
2.
编程题1——求单调递增最长子序列
设计一个O(n2)时间的算法,找出由n个数组成的序列的最长单调递增子序列。
输入格式:
输入有两行: 第一行:n,代表要输入的数列的个数 第二行:n个数,数字之间用空格格开
输出格式:
最长单调递增子序列的长度
输入样例:
在这里给出一组输入。例如:
5
1 3 5 2 9
输出样例:
在这里给出相应的输出。例如:
4
O(n²)的算法其实很容易想到,设原数列为数组num[n],我们定义dp数组的dp[i]为以数列中第i个数字为结尾的最长单调递增子序列长度,那么我们要找到这个值,可以遍历原数列的第1~i-1个数字,对于每个num[j],若有num[j]<num[i],那么就进行更新,dp[i]=max(dp[j]+1,dp[i]),因为第i个数字显然可以接在第j个数字的最长子序列后面,再判断新生成的子序列长度是否是当前最长即可。
还有一种O(nlogn)的做法,定义dp[i]为长度为i的单调递增子序列中的的最小结尾,首先初始化dp数组的所有元素为一个极大值,对于每一个num[i],我们可以在dp数组中找到一个dp[len]>=num[i],然后更新dp[len]的值,即以第i个数字作为长度为len的单调递增子序列的结尾,以更小结尾的子序列一定是更优的,因为可以接受更多的数字。最后出现过的len的最大值即为答案(由于数组下标从0开始,所以答案其实为len+1)。
其实到现在还并不是O(nlogn)的复杂度,因为dp数组的长度最大还是有可能达到n,不过由于dp数组显然具有递增的性质(小的数字肯定先更新前面的dp值),所以我们可以用二分查找来优化查找dp值的操作,优化后的复杂度即为O(nlogn)。
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long ll; const int MAX_N = 10050; int n,len,Index,num[MAX_N],dp[MAX_N]; int main(){ num[0] = -1e9; fill(dp,dp+MAX_N,1e9); scanf("%d",&n); for(int i = 1;i <= n;++i){ scanf("%d",&num[i]); } for(int i = 1;i <= n;++i){ Index = lower_bound(dp,dp+len+1,num[i]) - dp; dp[Index] = num[i]; if(Index > len){ len = Index; } } printf("%d",len + 1); return 0; }
lower_bound函数为STL自带的在一个有顺序的数组中二分查找确定值的一个函数
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。所以需要通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
编程题2——租用游艇问题
题目来源:王晓东,《算法设计与分析》
长江游艇俱乐部在长江上设置了n个游艇出租站1,2,…,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站i到游艇出租站j之间的租金为r(i,j),1<=i<j<=n。试设计一个算法,计算出从游艇出租站1 到游艇出租站n所需的最少租金。
输入格式:
第1 行中有1 个正整数n(n<=200),表示有n个游艇出租站。接下来的第1到第n-1 行,第i行表示第i站到第i+1站,第i+2站, ... , 第n站的租金。
输出格式:
输出从游艇出租站1 到游艇出租站n所需的最少租金。
输入样例:
在这里给出一组输入。例如:
3
5 15
7
输出样例:
在这里给出相应的输出。例如:
12
设rent[i][j]为出租站i到出租站j的租金,dp[i]为出租站i到出租站n所需的最少租金,那么对于每一个站点,我们可以枚举它与站点n直接的中间站,判断以该站点为中转是否更优,即,i+1<=j<=n,dp[i]=min(dp[i],rent[i][j]+dp[j])。
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long ll; const int MAX_N = 210, MAXIMUM = 1e9; int n,temp,rent[MAX_N][MAX_N]; int main(){ fill(*rent,*rent + 205 * 205, MAXIMUM); scanf("%d",&n); for(int i = 1;i < n;++i){ rent[i][i] = 0; for(int j = i + 1;j <= n;++j){ scanf("%d",&rent[i][j]); } } for(int i = n - 2;i >= 1;--i){ for(int j = i + 1;j <= n;++j){ rent[i][n] = min(rent[i][j] + rent[j][n], rent[i][n]); } } printf("%d",rent[1][n]); return 0; }
可以直接在rent数组上计算dp值,节省一点点空间和时间。
3.
结对编程很不错,合作愉快。