1.装载问题:(cw当前装载重量; bestw当前最优装载重量; c剩余集装箱重量)
void backtrack(int t)
{
if(t>n)//1下含一for循环,遍历叶子结点
{
for(j=1;j<=n;j++)
{
bestx[j]=x[j];
bestw=cw;
}
}
return;
r-=w[i];//4
if(cw+w[i]<=c)
{
w[i]=1;
cw+=w[i];
backtrack(t=1);
cw-=w[i];
}
if(cw+c>bestw)
{
w[i]=0;
backtrack(t+1);
}
r+=w[i];
}
*2.批处理作业调度问题(Tij作业i在机器j的处理时间;Fij作业i在机器j上完成处理的时间;)
void backtrack(int t)
{
if(t>n)
{
for(j=1;j<=n,j++)
{
bestx[j]=x[j];
bestf=f;
}
}
else
for(j=i;j<=n;j++)
{
f1+=m[x[i]][j];
f2[i]=((f2[i-1]>f1)?f2[i-1];f1)+m[x[j]][2];//f2+=f1;
f+=f2[i];
}
if(f<bestf)
{
swap(x[i],x[j]);
backtrack(i+1);
swap(x[i],x[j]);
}
f1-=m[x[i]][j];
f-=f2[i];
}
3.符号三角形问题
+、-个数均为n(n+1)/4个
void backtrack(int t)
{
if(count>half||t(t+1)/4-count>half) return;//注此例题约束条件放到了叶子结点检验前
if(t>n)
sum++;//处在叶子结点时
for(i=0;i<=1;i++)//0表+,1表-,此循环0、1一次性展开
{
p[1][t]=i;
count+=i;
for(j=2;j<=n;j++)
{
p[j][n-j+1]=p[j-1][n-j+1]+p[j-1][n-j+2];
count+=p[j][n-j+1;
}
backtrack(t-1);
for(j=2;j<=n;j++)
{
count-=p[j][n-j+1;
count-=i;
}
}
}
*4.n皇后问题
约束条件:不同行
可行解:不同列,不同对角线(即|i-j|=|x[i]-x[j]|)
boolean place(int k)
{
for(i=1;i<=k;i++)
if(math.abs(x[i]-x[j])==math.abs(i-j)||x[i]==x[j]) return false;//判断同列,同对角线
else true;
}//place函数
void backtrack(int t)
{
if(t>n)
sum++;
else
for(i=1;i<=n;i++)
{
x[t]=i //第t行皇后处于i列, x[i]=t;
if(place(t))
backtrack(k+1);//判定place不行,并继续执行子树
}
}
*5.0-1背包问题(类似装载问题)
注:在搜索解空间树时,只要左儿子结点是一个可行结点,搜索就进入左子树;
当右子树可能包含最优解时才进入右子树搜索,否则剪去
*bound()计算当前结点初的上界,当要进入右子树时才计算bound
void backtrack(int i)
{
if(I>n)
{
bestp=p;//当前结点即最大值
return;
}
//搜索子树
if(cw+w[i<=c])//是一个可行结点,进入左子树
{
cw+=w[i];//
cp+=p[i];//
backtrack(i+1);
cw-=w[i];
cp-=p[i];
}
if(bound(i+1)>bestp)//可能包含最优解,才进入右子树,否则不进入/bestp>=bestw
backtrack(i+1);//
}
double bound(int i)
{ //计算上界,贪心法
double cleft=c-cw;//cleft剩余容量,c背包容量,cw 当前重量
double bound=cp;//bound上界,cp当前价值
//以物品单位价值递减顺序装入背包
while(i<=n||w[i]<=cleft)
{
cleft-=w[i];
bound+=p[i];
i++;
}
//装满背包
if(i<=n)
bound+=p[i]*cleft/w[i];//切割部分装入背包
return bound;
}
6.最大团问题(无向连通图中任意两点有连接边 所含顶点数最多的团)
void backtrack(int i)//当前扩展结点
{
if(i>n)
{
for(j=1;j<=n;j++)
bestx[j]=x[j];//记录最优解
bestn=cn;//记录当前最大团包含点数
return;
}
//可行性约束函数
Boolean ok=true;
for(j=1;j<=n;j++)
if(x[j]==1&&!a[i][j])//i与j不连接
{
ok=false;
break;
}
if(ok)//进入左子树
{
x[i]=1;//路径信息
cn++;
backtrack(t+1);
cn--;
}
if(cn+n-i>bestn)//进入右子树
{
x[i]=0;
backtrack(t+1);
}
}
7.图的m着色问题
问题描述:无向连通图G需m种颜色使图中每两条边连接的两个顶点着色不同,求最小m值
可行性约束条件:顶点i与已着色的相连顶点颜色不同,不同颜色用x[i]表示
void backtrack(int t)
{
if(t>n)
{
sum++;
}
else
for(i=1;i<=m;i++)//从根节点开始依次展开所有的m个分支
{
x[t]=i;//分支信息赋值给x[t]
if(ok(t)=true backtrack(t+1))//判定第t个点选着第i种颜色是否可行
}
}
boolean ok(int k)
{
for(intj=1;j<=k,j++)
if(a[i][k]&&x[i]==x[k])//已用过该颜色
return falsed;
}//检查颜色可用性
8旅行售货员问题
问题描述:售货员到若干城市去推销商品,已知各城市之间的路程,要求经过每个城市一遍,最后回到住地的路线,使总的路程最短。
9.圆排列问题
11.电路板排列问题(T11、12偏难,会比较啰嗦)
12.连续邮资问题
问题描述:假设某国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。连续邮资问题要求对于给定的n和m,给出邮票面值的最佳设计,在1张信封上贴出从邮资1开始,增量为1的最大连续邮资区间。
例n=5,m=4时,面值为1,3,11,15,32的5种邮票可以贴出邮资的最大连续区间是1到70。
题目意思即在5种邮票种任选一到四张(可重复选择面值相同的邮票),用选出的邮票面值总和的数值可能出现的情况是1到70的连续区间
(比如选择一张1,数值为1;选两1,数值和为2;一张3or3张1,数值和为3;一张3和一张1or四张1,数值和为4;一张3和两种1数值和为5,两张3数值和为6;两张3和一张1数值和为7;...一张32+一张15+两张11=69;两张32+两张3=70)注:此段浅灰段的内容,是为了辅助理解题意,鄙人自己补充的,与题目无关,读不通勿喷
问题的解:n种面值从小到大排序,用x[1:n]表示
13.排列树搜索(3类)
①n个不同元素的全排列
②有n个重复元素的全排列
③n个不同元素去镜像的全排列(不含镜像的排列n!/2个元素)
算法设计:选定好首元素,再选尾元素,然后确定中间n-2个元素的全排列
排列数搜索总结:
①去镜像搜索时间耗费降低为原来的1/2
②一个n元素集合,有K个元素重复,重复度是P1,P2,…PK,
则无重复排列数是n!/(p1!p2!...pk!)
③回溯法之排列树,根据问题考虑是否需去重复,去镜像
注:backtrack(t) 中t为当前结点要扩展的下层的编号 ,用for()循环控制各方发的展开