アルゴリズムの設計と分析のための戦略演習を分割して征服する
問題6を解決するために戦略を分割して征服する:ラウンドロビンスケジュール問題
問題の説明
n = 2 ^ kのプレイヤーがラウンドロビンをプレイしたいと仮定して、次の要件を満たすゲームスケジュールを設計します。
- 各プレイヤーは、他のn-1人のプレイヤーと1回対戦する必要があります
- 各プレイヤーは1日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); //右上交角拷贝到左下角
}
}
問題7を解決するための戦略を分割して征服する:特別なボードカバー
問題の説明
2k×2kの正方形で構成されたチェス盤には、他の正方形とは異なる正方形が1つだけあり、この正方形を特殊正方形と呼び、ボードを特殊チェスボードと呼びます。
チェスボードカバレッジ問題では、図に示すように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の特別なチェッカーボードカバーです。少し分析してみましょう。整然とカバーしたい場合、問題は次のような十分に小さなサブ問題に分解する
ことです。同じ番号コードを使用して表現したいので、空のボックスが1つしかない場合、特別な正方形でない場合は、対応するドミノ番号を入力します。
分解では、我々は特別なコーナーで番号を配置する必要があり
、
それを追加し
、左上隅の右下隅、追加
追加し、右上の左下隅を追加し、右下隅の左上隅を下の右上隅左隅に、そして
継続この戦略は、分解中にも使用されます。
アルゴリズムの実装
#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);
}
}
このブログの他の記事で推奨されています
アルゴリズムの設計と分析のための戦略演習を分割して征服する(上)