目录
递归与分治策略
递归算法一般用于解决三类问题
(1)数据的定义是按递归定义的。
(2)问题的解法按递归算法实现,例如回溯算法。
(3)数据结构形式是按递归定义的,例如树的遍历、图的搜索。
一、斐波那契数列
递归算法:
int fib(int n)
{
if(n<=1) return 1;
return fib(n-1)+fib(n-2);
}
递推算法:
int fib[50];
void fibonacci(int n)
{
fib[0] = 1;
fib[1] = 1;
for(int i=2;i<=n;i++)
fib[i] = fib[i-1]+fib[i-2];
}
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a=0,b=1,c;
int n;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
c=a+b;
a=b;
b=c;
printf(" %d",c);
}
return 0;
}
5
1 2 3 5
二、集合的全排列问题
#include<bits/stdc++.h>
using namespace std;
void Perm(int list[],int k,int m)
{
if(k==m)
{
for(int i=0;i<=m;i++)
printf("%d ",list[i]);
printf("\n");
}
else
{
for(int j=k;j<=m;j++)
{
swap(list[k],list[j]);//换头1234 到 头为4 4231
Perm(list,k+1,m);//尾部全排列
swap(list[k],list[j]);//换回来
}
}
}
int main()
{
int list[6]={1,2,3,4,5,6};
Perm(list,0,3);
return 0;
}
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 3 2
1 4 2 3
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 3 1
2 4 1 3
3 2 1 4
3 2 4 1
3 1 2 4
3 1 4 2
3 4 1 2
3 4 2 1
4 2 3 1
4 2 1 3
4 3 2 1
4 3 1 2
4 1 3 2
4 1 2 3
三、整数划分问题
递归公式:
正整数n,划分数的最大加数s不超过m
即f(6,4)最大加数不超过4,划分为4+2,4+1+1...
1)f(1,m)=1,f(1,7)=1;
f(n,1)=1 ,f(7,1)=1;
2)f(n,m)=f(n,n) m>=n
f(3,5)=f(3,3)划分的最大加数不超过n
3)f(n,n)=1+f(n,n-1)
f(6,6)=1+f(6,5) 6+0,5+1....
4) f(n,m)=f(n,m-1)+f(n-m,m)
f(6,4)=f(6,3)+f(2,4)=f(6,3)+f(2,2)=f(6,2)+f(3,3)+f(2,2)=f(6,1)+f(5,1)+f(3,3)+f(2,2)
f(6,1)不空盘子+f(5,1)空一个+f(3,3)+f(2,2)
6个物品放入4个盘子的情况数量=6个物品全放入3个盘子的情况数量(空一个盘子)+2个物品放入4个盘子的情况(不空)
6个物品放入3个盘子的情况数量=空一个盘子、空两个盘子、不空盘子的情况数之和
包括6个物品放入2个盘子的情况
#include <bits/stdc++.h>
using namespace std;
int split(int n,int m)
{
if(n==1||m==1)return 1;
else if(n<m)return split(n,n);
else if(n==m)return split(n,n-1)+1;//空一个盘子 +不空盘子
else return split(n,m-1)+split(n-m,m);
}
int main()
{
int n,m;
//int s;
while(~scanf("%d%d",&n,&m))//多组数据
printf("%d\n",split(n,m));
//while(cin>>n)
// cout<<split(n,n)<<endl;
//scanf("%d%d",&n,&m);
//s=split(n,m);
//printf("%d",s);
return 0;
}
输入:6 4
输出:9
输入:6 3
输出:7
输入:2 2
输出:2
四、循环赛日程表
n=2^k个选手
1)每个选手必须与其他n-1个选手各赛一次
2)每个选手一天只能参赛一次
3)循环赛在n-1天结束
假设只有两个选手,则比赛一天结束,选手1第一天遇到选手2,同样的,选手2第一天遇到选手1。则一次为基础进行比赛日程安排,将选手分为两半,则n个选手的比赛日程安排可以由n/2个选手的比赛日程决定。递归用这种一分为二的策略对选手进行划分,直到剩下最后两个选手。
下图是8个选手的比赛日程安排,左上角和左下角的两块分别是选手1到4和5到8 前三天的比赛日程安排。据此,将左上角的数字抄到右下角,将左下角的小块数字抄到右上角,就能安排好选手后四天的日程。
for(r=1;r<n;r<<=1)//左移一位后的值赋给r 相当于*2
for(int i=0;i<n;i+=2*r)
{
copy(r,r+i,0,i,r);
copy(r,i,0,r+i,r);
}
这段代码的意思是指:
for(r=1;r<n;r<<=1)
这部分的循环控制变量r
是用来逐步扩大复制范围的。r<<=1
表示将r
左移一位(相当于乘以2),这样每次循环r
都会翻倍。当r
达到n
(即 2 的 k 次方)的一半时,循环结束。
for(int i=0;i<n;i+=2*r)
则是一个内层循环,它的目的是遍历数组a
的每一行。i
从0开始,每次增加的是两倍的r
,直到覆盖当前子矩阵的完整宽度。这样做是为了避免重复处理已经复制过的元素,因为每次外层循环,r
都会复制到新的区域。
#include<bits/stdc++.h>
using namespace std;
const int MAX=64;
int a[MAX][MAX];
void copy(int tox,int toy,int fromx,int fromy,int r)
{
for(int i=0;i<r;i++)
{
for(int j=0;j<r;j++)
{
a[tox+i][toy+j]=a[fromx+i][fromy+j];
}
}
}
void Table(int k)
{
int i,r;
int n=1<<k;//左移k个二进制位 十进制就是2^k
//初始化第一行
for(int i=0;i<n;i++)
{
a[0][i]=i+1;
}
for(r=1;r<n;r<<=1)//左移一位后的值赋给r 相当于*2
for(int i=0;i<n;i+=2*r)
{
copy(r,r+i,0,i,r);
copy(r,i,0,r+i,r);
}
}
void Out(int n)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
printf("%3d",a[i][j]);
}
printf("\n");
}
printf("\n");
return;
}
int main()
{
int k;
while(scanf("%d",&k)&&k)
{
int n=1<<k;
Table(k);
Out(n);
}
return 0;
}
输入:3
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
五、棋盘覆盖问题
在一个2^kⅹ2^k个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为特殊方格,且称该棋盘为一特殊棋盘。显然,特殊方格出现的位置有4^k 种情况,即k>=0,有4^k 种不同的特殊棋盘。用分治策略,合理规划棋盘,使划分后的子棋盘大小相同,并且每个子棋盘均含有一个特殊方格,从而将原问题分解为规模较小的棋盘覆盖问题。
k=1
1 | 0 |
1 | 1 |
k=2
2 | 0 | 3 | 3 |
2 | 2 | 1 | 3 |
4 | 1 | 1 | 5 |
4 | 4 | 5 | 5 |
将其划分为四个子棋盘,则四个子棋盘中只有一个子棋盘有特殊方格,为了将其他棋盘化为特殊棋盘,将一个L型骨牌覆盖在三个小棋盘的会合处,则可以将问题转换为4个较小规模的棋盘覆盖问题。
在用L型骨牌不断覆盖没有特殊方格的子棋牌会合处的过程中,我们在不断的用新的骨牌(t++)覆盖棋盘,直到棋盘只剩一个特殊方格。
#include<bits/stdc++.h>
using namespace std;
static int tile=1;
int board[1025][1025];
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
if(size==1)return;
int t = tile++;
int s = size/2;
//zuoshang
if(dr<tr+s&&dc<tc+s)
ChessBoard(tr,tc,dr,dc,s);
else
{
board[tr+s-1][tc+s-1]=t;
ChessBoard(tr,tc,tr+s-1,tc+s-1,s);
}
//youshang
if(dr<tr+s&&dc>=tc+s)
ChessBoard(tr,tc+s,dr,dc,s);
else
{
board[tr+s-1][tc+s]=t;
ChessBoard(tr,tc+s,tr+s-1,tc+s,s);
}
//zuoxia
if(dr>=tr+s&&dc<tc+s)
ChessBoard(tr+s,tc,dr,dc,s);
else
{
board[tr+s][tc+s-1]=t;
ChessBoard(tr+s,tc,tr+s,tc+s-1,s);
}
//youxia
if(dr>=tr+s&&dc>=tc+s)
ChessBoard(tr+s,tc+s,dr,dc,s);
else
{
board[tr+s][tc+s]=t;
ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
}
int main()
{
int i,j;
int k;
while(cin>>k)
{
int size = 1<<k;
int x,y;
scanf("%d%d",&x,&y);
board[x][y]=0;
ChessBoard(0,0,x,y,size);
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
printf("%d\t",board[i][j]);
}
printf("\n");
}
}
return 0;
}
输入:
2
0 1
输出:
2 0 3 3
2 2 1 3
4 1 1 5
4 4 5 5
本博客资料、代码来源于清华大学出版社算法设计与分析,本博客仅用于个人学习,可能存在纰漏,敬请批评指正。