========================================
1、1,2,3...n入栈,问有多少种出栈的可能性,递归式和解析式。
原理:
有:h(0)=1, h(1)=1, h(2)=2, h(3)=5, 14, ...
- 递推关系的解为: h(n)=C(2n,n)/(n+1) (n=1,2,3,...)
- 递推关系的另类解为: h(n)=c(2n,n)-c(2n,n+1)(n=1,2,3,...)
应用:
关键是把原问题分解成
不相交的子问题的并集。
举例:
- n个数的入栈问题——假如数k为第一个出栈的数,那么k把1~n分成了两组,1~k-1和k+1~n,而k可取1~n中的任意数,故:f(n) = f(0)*f(n-1) + f(1)*f(n-2) + f(2)*f(n-3) + ... + f(n-1)*f(0), 令f(0)=f(1)=1;我们有f(n) = h(n).
- 矩阵乘法的括号化问题——每次用两个括号,把a1~an分成两份,a1~ak,ak+1~an,k=1,2,3...n-1,而这两段又组成了两个独立的子问题。于是:f(n) = f(1)*f(n-1) + f(2)*f(n-2) + ... f(n-1)*f(1),令f(1)=1. 我们有f(n) = h(n-1).
- n个节点构造二叉树问题——此处应该是认为每个节点都一样。取出根节点,设左子树有k个节点k=0,1,2,...n-1,则右子树有n-k-1个节点,故:f(n) = f(0)*f(n-1) + f(1)*f(n-2) +... + f(n-1)*f(0),令f(0)=1;我们有f(n) = h(n).
- 常见的例子还有:买票找零问题,凸多边形三角划分问题,不越过对角线的上班路线问题。
========================================
2、八皇后问题——递归解和非递归解
下面两个程序分别来自于:
程序注释比较详细,不再赘述,直接上代码吧。
递归解
//
回溯法解八皇后问题。
/* Code by Slyar */
#include
<stdio.h>
#include
<stdlib.h>
#define
max
8
int
queen
[
max
],
sum
=0;
/* max
为棋盘最大坐标,queen[i]标识第i行的皇后放置在第queen[i]列
*/
void
show
()
/*
输出所有皇后的坐标
*/
{
int
i
;
for
(
i
= 0;
i
<
max
;
i
++)
{
printf
(
"(%d,%d) "
,
i
,
queen
[
i
]);
}
printf
(
"\n"
);
sum
++;
}
int
check
(
int
n
)
/*
检查当前列能否放置皇后
*/
{
int
i
;
for
(
i
= 0;
i
<
n
;
i
++)
/*
检查横排和对角线上是否可以放置皇后
*/
{
if
(
queen
[
i
] ==
queen
[
n
] ||
abs
(
queen
[
i
] -
queen
[
n
]) == (
n
-
i
))
{
return
1;
}
}
return
0;
}
void
put
(
int
n
)
/*
回溯尝试皇后位置
,n
为横坐标
*/
{
int
i
;
for
(
i
= 0;
i
<
max
;
i
++)
{
queen
[
n
] =
i
;
/*
将皇后摆到当前循环到的位置
*/
if
(!
check
(
n
))
{
if
(
n
==
max
- 1)
{
show
();
/*
如果全部摆好,则输出所有皇后的坐标
*/
}
else
{
put
(
n
+ 1);
/*
否则继续摆放下一个皇后
*/
}
}
}
}
int
main
()
{
put
(0);
/*
从横坐标为开始依次尝试
*/
printf
(
"%d"
,
sum
);
system
(
"pause"
);
return
0;
}
非递归解:
/**
* 回溯法解N 皇后问题
* 使用一个一维数组表示皇后的位置
* 其中数组的下标表示皇后所在的行
* 数组元素的值表示皇后所在的列
* 这样设计的棋盘,所有皇后必定不在同一行,于是行冲突就不存在了
* date : 2011-08-03
* author: liuzhiwei
**/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define QUEEN 8 //皇后的数目
#define INITIAL -10000 //棋盘的初始值
int a [QUEEN]; //一维数组表示棋盘
void init () //对棋盘进行初始化
{
int *p ;
for (p = a; p < a + QUEEN; ++ p)
{
* p = INITIAL ;
}
}
int valid (int row, int col) //判断第row 行第col列是否可以放置皇后
{
int i ;
for (i = 0; i < QUEEN; ++i ) //对棋盘进行扫描
{
if (a [i] == col || abs (i - row) == abs (a[ i] - col )) //判断列冲突与斜线上的冲突
return 0;
}
return 1;
}
void print () //打印输出 N皇后的一组解
{
int i , j;
for (i = 0; i < QUEEN; ++i )
{
for (j = 0; j < QUEEN; ++j )
{
if (a [i] != j) //a[i] 为初始值
printf("%c " , '.');
else //a[i]表示在第i 行的第a[i]列可以放置皇后
printf("%c " , '#');
}
printf("\n" );
}
for (i = 0; i < QUEEN; ++i )
printf("%d " , a[ i]);
printf("\n" );
printf("--------------------------------\n" );
}
void queen () //N皇后程序
{
int n = 0;
int i = 0, j = 0;
while (i < QUEEN)
{
while (j < QUEEN) //对i 行的每一列进行探测,看是否可以放置皇后
{
if(valid (i, j)) // 该位置可以放置皇后
{
a[i ] = j; //第i 行放置皇后
j = 0; //第 i行放置皇后以后,需要继续探测下一行的皇后位置,所以此处将 j清零,从下一行的第列开始逐列探测
break;
}
else
{
++ j; //继续探测下一列
}
}
if(a [i] == INITIAL) //第 i行没有找到可以放置皇后的位置
{
if (i == 0) //回溯到第一行,仍然无法找到可以放置皇后的位置,则说明已经找到所有的解,程序终止
break;
else //没有找到可以放置皇后的列,此时就应该回溯
{
-- i;
j = a [i] + 1; //把上一行皇后的位置往后移一列
a[i ] = INITIAL; //把上一行皇后的位置清除,重新探测
continue;
}
}
if (i == QUEEN - 1) //最后一行找到了一个皇后位置,说明找到一个结果,打印出来
{
printf("answer %d : \n" , ++n);
print();
//不能在此处结束程序,因为我们要找的是 N皇后问题的所有解,此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测。
//_sleep(600);
j = a [i] + 1; //从最后一行放置皇后列数的下一列继续探测
a[i ] = INITIAL; //清除最后一行的皇后位置
continue;
}
++ i; //继续探测下一行的皇后位置
}
}
int main (void)
{
init();
queen();
system("pause" );
return 0;
}
========================================
3、背包问题——设定完成时间限制的01背包问题
背包问题主要的参考资料非《背包九讲》莫属。
http://wenku.baidu.com/view/8b3c0d778e9951e79b892755.html
背包问题概括下来,精华在于01背包、完全背包、多重背包三个问题的解上面。
- 01背包: f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]}- 完全背包:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-cost]+weight}-
多重背包:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]} 这是最直观的方法,还有就是可以把k用二进制表示,这样可以降低时间复杂度。
procedure MultiplePack(cost,weight,amount)
if cost*amount>=V
CompletePack(cost,weight)
return
integer k=1
while k<num
ZeroOnePack(k*cost,k*weight)
amount=amount-k
k=k*2
ZeroOnePack(amount*cost,amount*weight)
对于二维费用的问题,直接再加一维即可。
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}
2012-10-03