算法设计与分析之分治策略练习
分治策略解决问题六:循环赛日程表问题
问题描述
设有n=2^k个选手要进行循环赛,设计一个满足以下要求的比赛日程表:
- 每个选手必须与其他n-1个选手各赛一次
- 每个选手一天只能赛一次
- 循环赛一共进行n-1天
8个选手的示例如下:
问题分析
由示例图可分析出此日程表可以以最小单位进行对角复制即可生成,请观看我做的演示图:
算法实现
#include <iostream>
#include <cmath>
#include <algorithm>
#define N 100
using namespace std;
//存放日程表
int schedule[N][N];
//拷贝方块
void copyBox(int,int,int,int,int);
//生成循环赛日程表
void generateRRS(int);
int main()
{
int n;
cout<<"请输入参加人数:";
cin>>n;
generateRRS(n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<schedule[i][j]<<"\t";
}
cout<<"\n\n";
}
return 0;
}
/*
拷贝方块实现
toi,toj为拷贝到的位置的左上角在二维数组中的位置
fromi,fromj为原始的位置的左上角在二维数组中的位置
size为拷贝方块的边长
*/
void copyBox(int toi,int toj,int fromi,int fromj,int size){
for(int i=0;i<size;i++)
for(int j=0;j<size;j++)
schedule[toi+i][toj+j] = schedule[fromi+i][fromj+j];
}
/*
生成循环赛日程表
n=2^k,选手个数,参数错误则提示并结束程序
*/
void generateRRS(int n){
double temp = log(n)/log(2);
if(temp != (int)temp || temp == 0){
cout<<"函数参数异常!";
exit(0);
}
for(int i=0;i<n;i++) //初始化第一行
schedule[0][i]=i+1;
for(int size=1;size<n;size*=2)
for(int i=0;i<n;i+=2*size) {
copyBox(size,size+i,0,i,size); //左上角拷贝到右下角
copyBox(size,i,0,size+i,size); //右上交角拷贝到左下角
}
}
分治策略解决问题七:特殊棋盘覆盖
问题描述
在一个2k×2k个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
对于给定的特殊棋盘,设计棋盘覆盖方案
例如:
输入:
2 // k,棋盘的边长为2k
0 1 //特殊方格的坐标( 用2k *2k 的矩阵表示一个棋盘)
输出:
2 0 3 3
2 2 1 3
4 1 1 5
4 4 5 5
问题分析
上图是一个完成的4*4的特殊棋盘覆盖,我们稍加分析,要想有条理的覆盖其问题莫过于把问题分解成足够小的子问题比如这样:
因为要用相同数字代码表示,我们还应分的更小,只有一个空时,如果不是特殊方格就填入相对应的骨牌数字。
一次分解时对于不含特殊方格我们需要分别在一个特殊的角落放一个编号,
左上角的右下角加,
右上角的左下角加,
右下角的左上角加,
左下角的右上角加,
继续分解时仍使用此策略。
算法实现
#include <iostream>
#include <cmath>
#include <algorithm>
#define N 100
using namespace std;
//棋盘
int chessboard[N][N];
//骨牌编号
int gno = 0;
//覆盖特殊棋盘函数
void putChessboard(int,int,int,int,int);
int main()
{
//棋盘大小
int n;
//特殊方格行列号
int si,sj;
cout<<"请输入棋盘大小:";
cin>>n;
//检测一下n
double temp = log(n)/log(2);
if(temp != (int)temp || temp == 0){
cout<<"棋盘大小异常!";
exit(0);
}
cout<<"请输入特殊方格位置:";
cin>>si>>sj;
putChessboard(0,0,si,sj,n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<chessboard[i][j]<<"\t";
}
cout<<"\n\n";
}
return 0;
}
/*
覆盖特殊棋盘函数
ti,tj 棋盘左上角的坐标
si,sj 特殊方格的位置
size 棋盘宽度 不符提示并退出程序
*/
void putChessboard(int ti,int tj,int si,int sj,int size){
if(size == 1) return;
//当前骨牌编号
int no = ++gno;
int mSize = size/2; // 分割棋盘
//处理左上角棋盘
if(si<ti+mSize && sj<tj+mSize)
putChessboard(ti,tj,si,sj,mSize);
else{
chessboard[ti+mSize-1][tj+mSize-1] = no;
putChessboard(ti,tj,ti+mSize-1,tj+mSize-1,mSize);
}
//处理左下角棋盘
if(si>=ti+mSize && sj<tj+mSize)
putChessboard(ti+mSize,tj,si,sj,mSize);
else{
chessboard[ti+mSize][tj+mSize-1] = no;
putChessboard(ti+mSize,tj,ti+mSize,tj+mSize-1,mSize);
}
//处理右上角棋盘
if(si<ti+mSize && sj>=tj+mSize)
putChessboard(ti,tj+mSize,si,sj,mSize);
else{
chessboard[ti+mSize-1][tj+mSize] = no;
putChessboard(ti,tj+mSize,ti+mSize-1,tj+mSize,mSize);
}
//处理右下角棋盘
if(si>=ti+mSize && sj>=tj+mSize)
putChessboard(ti+mSize,tj+mSize,si,sj,mSize);
else{
chessboard[ti+mSize][tj+mSize] = no;
putChessboard(ti+mSize,tj+mSize,ti+mSize,tj+mSize,mSize);
}
}