1.0-1背包问题两种方法:蛮力法与回溯法有何区别?时间渐近复杂度分别分为多少?有何区别?为什么?
区别:对于0/1 选择树 回溯法会对子树进行剪枝,降低时间复杂度
时间复杂度均为 O ( n ) = 2 n O(n)=2^{n} O(n)=2n
区别:在实际运算中,数据相同时,虽然它们的时间复杂度的上界函数相同,但回溯法的时间复杂度会优于蛮力法,在最坏情况下二者时间渐进复杂度才相同。
原因:回溯法会对子树进行剪枝以降低时间复杂度。
2.解决非0-1背包问题:
typedef struct{
char name;
float v;
float w;
float key;
}goods;
int n;vector<goods> g;
float nowweight=0;
float remainweight=10;
float nowvalue=0;
vector<int> bagG;
float bestV=0;
vector<int> bestS;
float shangjie=0;
void digui(int k)
{
int i,j=0,maxG;
float shangjie=0;
if(k>n)
{
if(nowvalue> bestV)
{
for(int i=0;i<n;i++)
{
bestS[i]=bagG[i];
}
bestV=nowvalue;
}
return;
}
maxG=remainweight/g[k].w;
shangjie=nowvalue+(remainweight-nowweight)*g[k].key;
if(bestV>=shangjie)return;
for(i=maxG;i>=0;i--)
{
if(nowweight+i*g[k].w <=remainweight )
{
nowweight += i*g[k].w;
nowvalue+=i*g[k].v;
bagG[k]=i;
j=k;
if(nowweight==remainweight)
{
for(j++;j<n;j++)
{
bagG[j]=0;
}
}
digui(j+1);
nowweight -= i*g[k].w;
nowvalue-=i*g[k].v;
bagG[k]=0;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;++i)
{
cin>>g[i].name>>g[i].w>>g[i].v;
}
for(int i=0;i<n;i++)
g[i].key= 1.0*g[i].v/g[i].w;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<i;j++)
if(g[j].key < g[j+1].key )
{
char tname=g[j].name;
g[j].name=g[j+1].name;
g[j+1].name=tname;
float t=g[j].v;
g[j].v=g[j+1].v;
g[j+1].v=t;
t=g[j].w;
g[j].w=g[j+1].w;
g[j+1].w=t;
t=g[j].key;
g[j].key =g[j+1].key ;
g[j+1].key =t;
}
}
for(int i=0;i<n;i++)
{
cout<<g[i].name<<endl;
}
digui(0);
cout<<bestV<<endl;
for(int i=0;i<n;i++)
{
cout<<g[i].name <<":"<< bestS[i]<<endl;
}
return 0;
}
时间复杂度
外层的递归时间复杂度为 O ( n ) O(n) O(n);内层的 for 循环最坏为 n / w n/w n/w 次; 则时间复杂度为 O ( n 2 / w ) O(n^2/w) O(n2/w)
旅行商问题两种方法:蛮力法与回溯法有何区别?时间渐近复杂度分别分为多少?有何区别?为什么?
区别:蛮力法会穷举所有可能的路径,而回溯法会在搜索的过程中进行剪枝,以减少搜索空间,从而提高效率。
时间复杂度均为 O ( ( n − 1 ) ! ) O((n-1)!) O((n−1)!)
区别:在实际运算中,一般情况下,回溯法的时间复杂度均优于蛮力法,除非是最坏情况下二者时间渐进复杂度才相同。
原因:蛮力法是思想最简单的穷举法,时间复杂度非常高,适用于小规模问题;而回溯法则在加入了剪枝策略以减少搜索空间,提高效率。
回溯法的优势在于能够在可接受的时间内找到较优解,尤其是在城市数量较大时,其效率要明显高于蛮力法。
4.旅行商问题的代码实现 利用贪心的思想的bfs
void bfs(int k){
priority_queue<Node> q;
Node node,t;
for(int i = 1; i <= n; i++){
node.x[i] = i;
}
node.v = 2;
node.len = 0;
node.rl = least;
node.sl = least;
q.push(node);
while(!q.empty()){
t = q.top();
q.pop();
int now = t.v;
if(t.v == n + 1){
if(m[t.x[now - 1]][k] != INT_MAX){
bestl = t.len + m[t.x[now - 1]][k];
for(int i = 1; i <= n; i++){
bestx[i] = t.x[i];
}
bestx[n + 1] = k;
return ;
}
}
for(int i = now; i<= n; i++){
if(m[t.x[now - 1]][t.x[i]] != INT_MAX && t.sl < bestl){
node.v = now + 1;
node.len = t.len + m[t.x[now - 1]][t.x[i]];
node.rl -= minlen[t.x[i]];
node.sl = node.rl + node.len;
for(int j = 1; j <= n; j++){
node.x[j] = t.x[j];
}
swap(node.x[i],node.x[now]);
q.push(node);
}
}
}
}
借助优先队列,每次选择路径最短的节点,再判断是否已选择所有的节点;若满足,则判断是否可以返回起始节点,更新最优路径长度 bestl
和最优路径顺序 bestx
;否则,从当前层开始遍历所有可能的下一个节点,对于每个可行的下一个节点,构造一个新的状态节点 node,更新其路径长度、剩余路径下界并通过swap函数交换新节点与当前节点。
int main(){
cin >> n >> e;
for(int i = 0; i <= n; i++){
minlen[i] = INT_MAX;
for(int j = 0; j <= n; j++){
m[i][j] = INT_MAX;
}
}
for(int i = 0; i < e; i++){
int v,u,len;
cin >> v >> u >> len;
m[v][u] = len;
m[u][v] = len;
minlen[v] = min(minlen[v], m[v][u]);
minlen[u] = min(minlen[u], m[u][v]);
}
for(int i = 1; i <= n; i++){
least += minlen[i];
}
bestl = INT_MAX;
bfs(1);
cout<<"best road:"<<endl;
for(int i = 1; i <= n + 1; i++){
cout << bestx[i] << " ";
}
cout << endl;
cout<<"min roadlength:"<<endl;
cout << bestl << endl;
}
计算所有节点最短路之和least后调用bfs搜索 即可得到最优路径
这里使用贪心不断更新函数的下界,但对于最优路径的上界,直接令
x
m
a
x
=
当前路径长度与剩余节点中最短路径之和
xmax=当前路径长度与剩余节点中最短路径之和
xmax=当前路径长度与剩余节点中最短路径之和,更加准确
bfs的时间复杂度 外层 每个节点均进行一次访问 内层 对当前节点的所有邻居节点进行遍历
内外两层复杂度均为 O ( n ) , O(n), O(n),总时间复杂度为 O ( n 2 ) O(n^2) O(n2)
空间复杂度
使用优先队列存储各节点,空间复杂度为
O
(
n
)
O(n)
O(n)
5.回溯法用于剪枝的约束条件:
由问题的评价函数决定,需要判断当前的路径或当前值是否是问题的解
分支限界法的剪支约束条件:
由 1)上界函数:即以当前结点为根的可行性解可能达到的极值;
2)限界值:搜索到某一结点时已得到的最优解;
3)评价函数:判定当前路径或值是否为解;
剪枝条件:
1)该结点的上界小于界限值,即再往下搜索也不可能有更优的值。
2)该结点不含于任何可行解,不能满足评价函数。
3)该节点代表的可行解的子集只包含一个单独的点。
使用回溯法和分支限界法解决装载问题有何不同?
回溯法使用较为直接的dfs搜索,解决装载问题时,先将每个货物依次装入货车,判断其重量是否满足限制条件,若满足则对下一个货物进行操作,否则回溯到上一个状态。回溯法会对所有可能的解空间进行搜索。
其时间复杂度一般为 O ( 2 n ) O(2^{n}) O(2n)
分支限界法虽然也采用dfs,但可以使用多种剪枝方式减少搜索空间提高搜索效率。解决装载问题时,可以优先选择更可能出现最优解的分支进行搜索,同时使用上界函数辅助剪枝,减少无效的搜索。
时间复杂度为 O ( n ∗ w ) O(n*w) O(n∗w) n − 货物数量 w − 船只载重 n-货物数量 w-船只载重 n−货物数量w−船只载重