算法分析与设计
m[i][j]=max(m[i+1][j], m[i+1][j-w[i]]+v[i]);
}
m[1][c]=m[2][c]; //当0<=j<w[1]
if (c>=w[1]) //当w[1] <= j
m[1][c]=max(m[1][c], m[2][c-w[1]]+v[1]);
}
3)根据最优值计算最优解
//进行(2)式赋值
for( int i=n-1; i>1; i--) {
jMax=min(w[i]-1,c);
for( int j=0; j<=jMax; j++)
m[i][j]=m[i+1][j]; //当0<=j<w[i]
for(int j=w[i]; j<=c; j++) //当w[i] <= j <= c
Sort(n,v,w); //按物品单位重量的价值降序排序。
int i;
for (i=1;i<=n;i++) x[i]=0;
float c=M;
for (i=1;i<=n;i++) {
if (w[i]>c) break;
x[i]=1;
c-=w[i];
}
if (i<=n) x[i]=c/w[i];//部分装入物品i。
x[n]=(m[n][c]>0)? 1:0;
}
最优子结构性质。
4.为什么用分治法设计的算法一般有递归调用?
解答:
子问题的规模还很大时,必须继续使用分治法,反复分治,必然要用到递归。
5.简述分治法所能解决的问题一般应具有的特征。
解答:
1)该问题的规模缩小到一定的程度就可以容易地解决;
2)该问题具有最优子结构性质;
3)利用该问题分解出的子问题的解可以合并为该问题的解;
0-1背包问题:在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。
背包问题:与0-1背包问题所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包。
这2类问题都具有最优子结构性质,极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解。
9.简述回溯法与分支限界法的相同点。
解答:
分支限界法与回溯法的相同点是:都是一种在问题的解空间树T中搜索问题解的算法。
二、算法分析解答题
附:参考答案
1.简述0-1背包问题和背包问题的差别,描述求解背包问题的贪心算法。
解答:
给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
}
2.考虑金钱兑换问题:有一个货币系统,它有n种硬币,它们的面值为v1,v2,…,硬币的数目最少。更形式地,我们要让下面的量 在约束条件 下最小。其中x1, x2, …, xn是非负整数。
1)用贪心算法求解该问题;
2)如果硬币的面值有1分,5分,7分和11分,给出反例说明用贪心算法并不总是有效的。
用贪心算法解背包问题的基本问题:首先计算每种物品单位重量的价值Vi/Wi,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过C,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直地进行下去,直到背包装满为止。
void Knapsack(int n,float M,float v[],float w[],float x[]){
4)该问题所分解出的各个子问题是相互独立的。
6.在回溯法中,为了避免无效的搜索,通常采用哪两种剪枝策略?
解答:
约束剪枝,限界剪枝。
7.给定如下二分搜索算法,请分析算法的复杂性。
int BinarySearch(Type a[], const Type& x, int l, int r){
while (r >= l){
解答:
1)定义最优值
设m(i, j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优解的值。由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下:
2)计算最优值
void knapsack(int v[ ], int w[ ], int c, int m[ ][ ])
int m = (l+r)/2;
if (x == a[m]) return m;
if (x < a[m]) r = m-1; else l = m+1;
}
return -1;
}
解答:
整个算法在最坏情况下的计算时间复杂性为O(logn)。
8.回溯法的搜索特点是什么?
解答:
在解空间树上跳跃式地深度优先搜索,即用判定函数考察x[k]的取值,如果x[k]是合理的就搜索x[k]为根节点的子树,如果x[k]取完了所有的值,便回溯到x[k-1]。
2.动态规划算法的基本思想是什么?它和分治法有什么区别和联系?
解答:
动态规划算法的基本思想为:该方法的思路也是将待求解的大问题分成规模较小的子问题,但所得的各子问题之间有重复子问题,为了避免子问题的重复计算,可依自底向上的方式计算最优值,并根据子问题的最优值合并得到更大问题的最优值,进而可构造出所求问题的最优解。
void traceback( int m[ ][ ], int w[ ], int c, int x[ ])
{ int n=w.length-1;
for(int i=1; i<n; i++)
if (m[i][c]==m[i+1][c]) x[i]=0; //物品i没有被选择
else { x[i]=1; c-=w[i]; }
解答:
1)贪心伪代码为:
Greedy_exchange ( int v[], int y, int num[], int n){
int i=0;
for( i=0;i<= n;i++) num[i]=0;
Sort(v); //从大到小排序
i=0;
while (y>0){
num[i]=y/v[i]; y=y %v[i++];
}
return num;
}
2)反例:假如要给顾客找15分钱,按照上述贪心策略,则应将15分分解为:11分+1分+1分+1分+1分。而事实上15分可分解为:5分+5分+5分。显然后者的张数更少。
3.给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?请写出动态规划算法求解0-1背包问题。
《算法分析与设计》2020年03月考试考前练习题
一、简答题
附:参考答案
1.何谓P、NP、NPC问题。
解答:
P(Polynomial问题):也即是多项式复杂程度的问题。
NP就是Non-deterministic Polynomial的问题,也即是多项式复杂程度的非确定性问题。
NPC(NP Complete)问题,这种问题只有把解域里面的所有可能都穷举了之后才能得出答案,这样的问题是NP里面最难的问题,这种问题就是NPC问题。
{
int n=v.length-1;
int jMax=min(w[n]-1,c);
//进行(1)式赋值
for( int j=0; j<=jMax; j++)
m[n][j]=0; //当0<=j<w[n]
for( int j=w[n]; j<=c; j++) // j为背包容量
m[n][j]=v[n]; //当w[n] <= j,m(n,j)赋值
分治法也是将待求解的大问题分成若干个规模较小的相同子问题,即该问题具有最优子结构性质。规模缩小到一定的程度就可以容易地解决。所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题;利用该问题分解出的子问题的解可以合并为该问题的解。
3.贪心算法和动态规划算法都要求问题具有共同的性质是?
解答: