1.程序、算法、软件是不完全等价的。
2.最优化问题不是都可以用动态规划来求解,使用动态规划要满足(子问题间有依赖关系;有最优子结构)。
3.分治策略是先把复杂的问题自顶向下求解,然后自底向上合并,不一定要用递归,用迭代也可以。
4.动态规划能求解的问题,贪心策略不一定能求解,反之也不行。(如0-1背包问题可以用动态规划但不能用贪心;背包问题可以用贪心但不能用递归)。
5.两个序列的公共子序列和最长公共子序列都不唯一。
6.棋盘覆盖问题8*8的棋盘用(64-1)/3=21块骨牌。
7.哈夫曼算法是贪心算法,每次选择频率最高的两个节点合并成子树。
8.不同的排序算法针对不同实例,速度效率不一样。
9.常用的时间复杂度。
(PPT ch1)
http://wjhsh.net/xymqx-p-3702921.html
//回溯法搜索子集树
void Backtrack(int t){
{if(t>n)output(x);else{for(int i=0;i<=1;i++) //子集树只考虑取或者不取,所以解向量只有0或1;{x[t]=i; if(Constraint(t)&&Bound(t))Backtrack(t+1);}}
}
//回溯法搜索排列树
void Backtrack(int t){
{if(t>n)output(x);else{for(int i=0;i<=n;i++){Swap(x[t],x[i]); //把当前递归层的元素依次和其他元素交换if(Constraint(t)&&Bound(t))Backtrack(t+1); //进入下一层递归Swap(x[t],x[i]); //回溯时候恢复之前的交换}}
}
约束函数:在扩展节点处剪去不满足约束的子树
限界函数:用限界函数剪去不能得到最优解的子树
都是剪去不必要搜索的子树,约束函数剪去的是无解的子树,而限界函数剪去的是得不到最优解的子树。
相同:都是把大问题转化为子问题来解决
不同:递归分治的子问题相互独立,动态规划的子问题是相互依赖的,有最优子结构
改写二分搜索算法,使得当搜索元素x不在数组中时,返回小于x的最大元素位置i和大于x的元素位置j。当搜索元素在数组中时,i和j相同,均为x在数组中的位置。
二分搜索方法充分利用了元素间的次序关系,采用分治策略,可以在最坏情况下用O(logn)时间完成搜索任务,将n个元素分成个数大致相同的两半,取a[n/2]与x进行比较,若x=a[n/2],则找到x,算法终止;x=a[n/2],则只在数组左半部递归的搜索;若x>a[n/2],则只在数组右半部递归的搜索。
int Binary_Search(int a[],int length, int x) //a是搜索数组,x为搜索元素
{int i = 0, j = 0; //用来输出结果int flag = -1;//标志位int high = length - 1; // 数组的右边界int mid = 0; //中间值的下标int low = 0; //数组的左边界while (low <= high){mid = (low + high) / 2;if (a[mid] == x){flag = middle;}if (a[mid] < x){low = mid + 1;}else{top = mid - 1;}}if (flag == -1){i = high;j = low;}else{i = j = flag ;}cout<
void dp(int D[],int X[],int R[],n)
{ //X[i]存储第i个数字的值,D[i]表示以x[i]为开头的最大字段和的值;R[]D[n]=X[n]; //最后一位开头的最长子段和只能是本身R[n]=n; //最后一位的右端点只能是nfor(int i=n-1;i>=n;i--){ //最后一个位置的数字作为初值,从倒数第二个位置开始从后向前递推if(D[i+1]>0) D[i]=X[i]+D[i+1]; //如果以i+1位置开头的最大字段和大于0,那么以i位置开头最大字段和就是当前位置的数值+后面的最大字段和的值。else{D[i]=X[i]; //如果以i+1位置开头的最大字段和小于0;R[i]=i; //此时最大字段和的右端就是i本身}}
}void MaxSum(int D[],int R[]}
{S_max=D[1] //先假设以第一个数字开头的最大字段和为全局最大字段和for(int i=2;i<=n;i++){if(S_maxl=i;r=R[i];}}cout<<"最大字段和从位置"<
O(n)
void Loading(int x[], Type w[], Type c, int n)
{int *t = new int [n+1]; //动态数组t用来存放排序后的货物Sort(w, t, n); //将集装箱按重量把数组w非减排序存放到数组t中for ( int i = 1; i <= n; i++ ) x[i] = 0; //决策变量初始化for ( int i = 1; i <= n && w[t[i]] <= c; i++ ) {x[t[i]] = 1; //决策变量的对应位置置为1c -= w[t[i]]; //更新当前的荷载量} //在剩余集装箱中选择最轻的装船,直至超重
}
O(nlogn) 主要来自把货物按重量排序的算法sort()
答:排列树,因为每个城市都要访问。
//旅行商问题回溯算法的实现
//形参t是回溯的深度,从2开始
void Backtrack(int t)
{//到达叶子结点的父结点if(t==n){if(a[x[n-1]][x[n]]!= NoEdge && a[x[n]][1]!= NoEdge && (cc + a[x[n-1]][x[n]]+a[x[n]][1]for(int i=1; i<=n; i++)bestx[i] = x[i];bestc = cc + a[x[n-1]][x[n]] + a[x[n]][1];}return;}
else {for(int i=t; i<=n; i++){if(a[x[t-1]][x[i]]!= NoEdge &&(cc + a[x[t-1]][x[i]]< bestc||bestc == NoEdge)){swap(x[t],x[i]);cc += a[x[t-1]][x[t]];Backtrack(t+1);cc -= a[x[t-1]][x[t]];swap(x[t],x[i]);}}}
}
//旅行商问题分支限界法的实现
template
Type Traveling::BBTSP(int *v, Type **G, int tn, Type tNoEdge)
{priority_queue > pq;MinHeapNode E, N;Type bestc, cc, rcost, MinSum, *MinOut, b;int i, j;a = G;n = tn;NoEdge = tNoEdge;MinSum = 0; //最小出边费用和MinOut = new Type[n+1]; //计算MinOut[i]=顶点i的最小出边费用for(i = 1; i <= n; i++){MinOut[i] = NoEdge;for(j = 1; j <= n; j++)if(a[i][j] != NoEdge && (a[i][j] < MinOut[i] || MinOut[i] == NoEdge))MinOut[i] = a[i][j];if(MinOut[i] == NoEdge) //无回路return NoEdge;MinSum += MinOut[i];}//初始化E.s = 0;E.cc = 0;E.rcost = MinSum;E.x = new int[n];for(i = 0; i < n; i++)E.x[i] = i+1;bestc = NoEdge;//搜索排列空间树while(E.s < n-1) //非叶结点{if(E.s == n-2) //当前扩展结点是叶结点的父结点 再加2条边构成回路{ //所构成回路是否优于当前最优解if(a[E.x[n-2]][E.x[n-1]] != NoEdge && a[E.x[n-1]][1] != NoEdge &&(E.cc+a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1] < bestc || bestc==NoEdge)){//费用更小的路bestc = E.cc + a[E.x[n-2]][E.x[n-1]] + a[E.x[n-1]][1];E.cc = bestc;E.lcost = bestc;E.s++;pq.push(E);}elsedelete []E.x; //舍弃扩展结点}else //产生当前扩展结点儿子结点{for(i = E.s+1; i < n; i++)if(a[E.x[E.s]][E.x[i]] != NoEdge){//可行儿子结点cc = E.cc + a[E.x[E.s]][E.x[i]]; //当前费用rcost = E.rcost - MinOut[E.x[E.s]]; //更新最小出边费用和b = cc + rcost; //下界if(b < bestc || bestc == NoEdge) //子树可能含最优解 结点插入最小堆{N.s = E.s + 1;N.cc = cc;N.lcost = b;N.rcost = rcost;N.x = new int[n];for(j = 0; j < n; j++)N.x[j] = E.x[j];N.x[E.s+1] = E.x[i]; //获得新的路径N.x[i] = E.x[E.s+1];pq.push(N); //加入优先队列}}delete []E.x; //完成结点扩展}if(pq.empty()) //堆已空break;E = pq.top(); //取下一扩展结点pq.pop();}if(bestc == NoEdge) //无回路return NoEdge;for(i = 0; i < n; i++) //将最优解复制到v[1:n]v[i+1] = E.x[i];while(pq.size()) //释放最小堆中所有结点{E = pq.top();pq.pop();delete []E.x;}return bestc;
}