第五天
回溯法:
回溯跟递归都是程序员考试里常出现的问题,大家必须掌握!
回溯法的有关概念:
1) 解答树:叶子结点可能是解,对结点进行后序遍历.
2) 搜索与回溯
五个数中任选三个的解答树(解肯定有三层,至叶子结点):
ROOT 虚根
/ / | / /
1 2 3 4 5
/ | | / / | / // |
2 3 4 5 3 4 5 4 5 5
/|/ // | // | |
3 4 5 4 5 5 4 5 5 5
回溯算法实现中的技巧:栈
要搞清回溯算法,先举一个(中序遍历二叉树的非递归算法)来说明栈在非递归中所起的作用。
A 过程:push()->push()->push()->push()栈内结果:ABDE(E为叶子,结束进栈)
/ / pop() ABD(E无右孩子,出栈)
B C pop() AB(D无右孩子,出栈)
// pop() A(B有右孩子,右孩子进栈)
D F . .
/ // . .
E G H . .
/ . .
I 最后结果: EDBGFIHAC
简单算法:
…
if(r!=NULL) //树不空
{ while(r!=NULL)
{ push(s,r);
r=r->lch; //一直向左孩子前进
}
while(!empty(s)) // 栈非空,出栈
{ p=pop(s);
printf(p->data);
p=p->rch; //向右孩子前进
while(p!=NULL)
{ push(s,p);
p=p->lch; //右孩子进栈
}
}
} //这就是传说中的回溯,嘻嘻……没吓着你吧
5选3问题算法:
思想: 进栈:搜索
出栈:回溯
边建树(进栈)边遍历(出栈)
基本流程:
太复杂了,再说我不太喜欢用WORD画图(有损形象),以后再整理!
程序: n=5;r=3
……
init(s) //初始化栈
push(s,1) //根进栈
while(s.top<r-1)&&(s.data[s.top]!=n) //有孩子
push(s,s.data[s.top]+1); //孩子入栈
while(!empty(s))
{ if(s.top=r-1)
判断该"解"是否为解.
x=pop(s); //保留x,判断是否为最大值n,如果是n,则出栈
while(x==n)
x=pop(s);
push(s,x+1);
while(s.top<r-1)&&(s.data[s.top]!=n)
push(s,s.data[s.top]+1);
}
背包问题: TW=20 , w[5]={6,10,7,5,8}
解的条件:1) 该解答树的叶子结点
2) 重量最大
解答树如下: ROOT
/ | | | /
6 10 7 5 8
/ | | / / | / / / |
10 7 5 8 7 5 8 5 8 8
| | |
5 8 8
程序:
temp_w 表示栈中重量和
…
init(s); //初始化栈
i=0;
While(w[i]>TW)
i++;
If(i==n) Return -1; //无解
Else {
Push(s,i);
Temp_w=w[i];
i++;
while(i<n)&&(temp_w+w[i]<=TW)
{ push(s,i);
temp_w+=w[i];
i++;
}
max_w=0;
while(!empty(s))
{ if(max_w<temp_w)
max_w=temp_w;
x=pop(s);
temp_w-=w[x];
x++;
while(x<n)&&(temp_w+w[x]>TW)
x++;
while(x<n)
{ push(s,x);
temp_w=temp_w+w[x];
x++;
while(x<n)&&(temp_w+w[x]>TW)
x++;
}
}
请大家思考:四色地图问题,比如给中国地图涂色,有四种颜色,每个省选一种颜色,相邻的省不能取同样的颜色.不许偷懒,不能选人口不多于xxxxW的"大国"哦!如果真的有一天台湾独立了,下场就是这样了,一种颜色就打发了,不过台湾的程序员们赚到了,省事!呵呵。
贪婪法:
不求最优解,速度快(以精确度换速度)
例:哈夫曼树,最小生成树
装箱问题:
有n个物品,重量分别为w[n],要把这n个物品装入载重为TW的集装箱内,需要几个集装箱?
思想1:对n个物品排序
拿出第1个集装箱,从大到小判断能不能放。
2 …
3 …
. …
. …
思想2: 对n个物品排序
用物品的重量去判断是否需要一只新箱子,如果物品重量小于本箱子所剩的载重量,则装进去,反之则取一只新箱子。
程序:
count=1;qw[0]=TW;
for(i=0;i<n;i++)
{
k=0;
while(k<count)&&(w[i]>qw[k])
k++;
if(w[i]<=qw[k])
qw[k]=qw[k]-w[i];
code[i]=k; //第i个物品放在第k个箱子内
else
{count++; //取一个新箱子
qw[count-1]=TW-w[i];
code[i]=count-1;
}
}
用贪婪法解背包问题:
n个物品,重量:w[n] 价值v[i]
背包限重TW,设计一个取法使得总价值最大.
方法:
0 1 2 3 … n-1
w0 w1 w2 w3 … wn-1
v0 v1 v2 v3 … vn-1
v0/w0 … v(n-1)/w(n-1) 求出各个物品的"性价比"
先按性价比从高到低进行排序
已知:w[n],v[n],TW
程序:
…
for(I=1;I<n;I++)
d[i]=v[i]/w[i]; //求性价比
for(I=0;I<n;I++)
{ max=-1;
for(j=0;j<n;j++)
{ if(d[j]>max)
{ max=d[j];x=j; }
}
e[i]=x;
d[x]=0;
}
temp_w=0;temp_v=0;
for(i=0;i<n;i++)
{ if(temp_w+w[e[i]]<=TW)
temp_v=temp_v+v[e[v]];
}
分治法:
思想:把规模为n的问题进行分解,分解成几个小规模的问题.然后在得到小规模问题的解的基础上,通过某种方法组合成该问题的解.
例:数轴上有n个点x[n],求距离最小的两个点.
分:任取一点,可以把x[i]这n个点分成两个部分
小的部分 分点 大的部分
|_._.__.__.____._|__._._.__._.__._______._.__._._.__.___._____._|
治:解=min{小的部分的距离最小值;
大的部分的距离最小值;
大的部分最小点和小的部分最大点这两点之差;}