算法设计与分析(一)——递归与分治

 

目录

A、二分查找

B、归并排序

 C、快速排序

 D、走迷宫

提示:

E、穷举n位2进制数

F、穷举所有排列

提示: 

G、循环赛日程表 

H、求第k小数

NOJ 2018.9.21

A、二分查找

时限:1000ms 内存限制:10000K  总时限:3000ms
描述给定一个单调递增的整数序列,问某个整数是否在序列中。
输入第一行为一个整数n,表示序列中整数的个数;第二行为n(n不超过10000)个整数;第三行为一个整数m(m不超过50000),表示查询的个数;接下来m行每行一个整数k。
输出每个查询的输出占一行,如果k在序列中,输出Yes,否则输出No。
输入样例5
1 3 4 7 11
3
3
6
9
输出样例Yes
No
No
#include <iostream>

using namespace std;

int a[10001];
int n;
int check[50001];
int m;

void getdata(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	cin >> m;
	for(int i = 0; i < m; i++){
		cin >> check[i];
	}
}

int search(int c){
	int left = 1, right = n;
	int mid;
	while(left <= right){
		mid = (left + right) / 2;
		if(a[mid] == c){
			return mid;
		}
		else if(a[mid] > c){
			right = mid - 1;
		}
		else{
			left = mid + 1;
		}
	}
	return 0;
}

int main(){
	getdata();
	for(int i = 0; i < m; i++){
		int ret = search(check[i]);
		if(ret){
			cout << "Yes" << endl;
		}
		else{
			cout << "No" << endl;
		}
	}
	return 0;
}

 

B、归并排序

时限:1000ms 内存限制:10000K  总时限:3000ms

描述给定一个数列,用归并排序算法把它排成升序。
输入

第一行是一个整数n(n不大于10000),表示要排序的数的个数;
下面一行是用空格隔开的n个整数。

输出输出排序后的数列,每个数字占一行。
输入样例5
3 2 1 4 5
输出样例1
2
3
4
5

 

#include <iostream>

using namespace std;
void copy(int left, int right);
void merge(int left, int i, int right);
int n;
int a[10000];
int b[10000];

void mergeSort(int left, int right)
{
	 if(left < right){
	 	int i = (left + right)/2;
	 	mergeSort(left, i);
	 	mergeSort(i + 1, right);
	 	merge(left, i, right);
	 	copy(left,right);
	 }
}

void merge(int left, int i, int right)
{
	int save_begin = left;
	int left_end = i++;
	int right_end = right;
	while((left <= left_end) && (i <= right_end)){
		if(a[left]>a[i])
			b[save_begin++] = a[i++];
		else
			b[save_begin++] = a[left++];
	}
	if(left <= left_end)
		while(left <= left_end)
			b[save_begin++] = a[left++];
	else
		while(i <= right_end)
			b[save_begin++] = a[i++];
}

void copy(int left, int right){
	while(left <= right){
		a[left] = b[left];
		left++;
	}
}

int main()
{
	cin >> n;
	for(int i = 0; i < n; i++)
		cin >> a[i];
	mergeSort(0, n-1);
	for(int i = 0; i < n; i++)
		cout << a[i] << endl;	
	return 0;
} 

 C、快速排序

时限:1000ms 内存限制:10000K  总时限:3000ms
描述给定一个数列,用快速排序算法把它排成升序。
输入第一是一个整数n(n不大于10000),表示要排序的数的个数;下面一行是用空格隔开的n个整数。
输出输出排序后的数列,每个数字占一行。
输入样例5
3 2 1 4 5
输出样例1
2
3
4
5

 

#include <iostream>

using namespace std;

int a[10005];
int n;

void getdata(){
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> a[i];
}

void output()
{
    for(int i = 0; i < n; i++)
        cout << a[i] << endl;
}

int qsort(int left, int right){
    int pivotkey = a[left];
    while(left < right){
        while((left < right) && (a[right] >= pivotkey))right--;
        a[left] = a[right];
        while((left < right) && (a[left] <= pivotkey))left++;
        a[right] = a[left];
    }
    a[left] = pivotkey;
    return left;
}

void quicksort(int left, int right){
    if(left < right){
        int p = qsort(left, right);
        quicksort(left, p - 1);
        quicksort(p + 1, right);
    }
}

int main(){
    getdata();
    quicksort(0, n-1);
    output();
}

 

 D、走迷宫

 

时限:1000ms 内存限制:10000K  总时限:3000ms

描述判断是否能从迷宫的入口到达出口
输入先输入两个不超过20的正整数表示迷宫的行数m和列数n,再输入口和出口的坐标,最后分m行输入迷宫,其中1表示墙,0表示空格每个数字之间都有空格。
输出只能向上、下、左、右四个方向走若能到达,则输出”Yes”,否则输出”No”,结果占一行。
输入样例3 3
0 0
2 2
0 0 0
1 1 0
0 1 0
输出样例

Yes

提示:

从起点开始深搜,找到联通的地方置为2,直至深搜结束;查看终点的值是否改变。

#include <iostream>

using namespace std;

int m, n;
int a[20][20];
int b_x, b_y;
int e_x, e_y;

void input(){
    cin >> m >> n;
    cin >> b_x >> b_y;
    cin >> e_x >> e_y;
    for(int i = 0; i < m; i++)
        for(int j = 0; j < n; j++)
            cin >> a[i][j];
}

void output(){
    if(a[e_x][e_y] == 2)
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
}

void dfs(int x, int y){
    //cout << x << " " << y <<endl;
    if((x >= 0) && (y >= 0) && (x < m) && (y < n)){
       if(a[x][y])
            return;
       else{
            a[x][y] = 2;
            dfs(x,y-1);//shang
            dfs(x-1,y);//zuo
            dfs(x,y+1);//xia
            dfs(x+1,y);//you
       }
    }
    else
        return;
}

int main(){
    input();
    dfs(b_x, b_y);
    output();
    return 0;
}

E、穷举n位2进制数

时限:100ms 内存限制:10000K  总时限:300ms

描述输入一个小于20的正整数n,要求按从小到大的顺序输出所有的n位二进制数,每个数占一行。
输入输入一个小于20的正整数n。
输出按从小到大的顺序输出所有的n位二进制数,每个数占一行。
输入样例3
输出样例000
001
010
011
100
101
110
111

 

#include <iostream>

using namespace std;

int n;
char s[20];

void dfs(int m){
	if(m==n){
		s[m] = 0 ;
		cout << s << endl;
	}
	else{
		for(int i = 0; i < 2; i++){
			s[m] = i + '0';
			dfs(m+1);
		}
	}
}

int main()
{
	cin >> n;
	dfs(0);
	return 0;
}

F、穷举所有排列

时限:100ms 内存限制:10000K  总时限:300ms

描述输入一个小于10的正整数n,按把每个元素都交换到最前面一次的方法,输出前n个小写字母的所有排列。
输入输入一个小于10的正整数n。
输出按把每个元素都交换到最前面一次的方法,输出前n个小写字母的所有排列。
输入样例3
输出样例abc
acb
bac
bca
cba
cab

提示: 

基于回溯、分治思想;

把全部元素换到最前面一次,先确定前k层元素,再进入k+1层,依次与剩余所有元素交换(包括自身),交换一次后进入下一层,直至进入第n层递归,此时确定整个序列;

递归返回后,再次还原至原有序列

#include <iostream>

using namespace std;

char a[20];
int n;

void change(int i, int j){
    char t = a[i];
    a[i] = a[j];
    a[j] = t;
}

void dfs(int m){
    if(m == n){    //当固定了前n-1个元素,即确定排列的顺序,输出
        cout << a << endl;
    }
    else{
        for(int i = m; i < n; i++){    //确定前m-1个位置的元素
            change(i, m);    //交换下标为i和m位置的元素
            dfs(m + 1);    //递归下一层
            change(i, m);    //还原
        }
    }
}

int main(){
    for(int i = 0; i < 10; i++){
        a[i] = i + 'a';
    }
    cin >> n;
    a[n]=0;  //初始化为字符串,以便输出
    dfs(0);    //开始排序
    return 0;
}

//以下算法是另一种排序

/*
int n;
char a[20];
char s[10];
int used[10];

void init(){
    cin >> n;
    for(int i = 0; i < 10; i++){
        a[i] = i + 'a';
    }
}

void dfs(int m){
    if(m == n)
    {
        s[m] = 0;
        cout << s << endl;
    }
    else{
        for(int i = 0; i < n; i++){
            if(used[i] == 0){
                s[m] = a[i];
                used[i] = 1;
                dfs(m+1);
                used[i] = 0;
            }
        }
    }
}

int main(){
    init();
    dfs(0);
    return 0;
}
*/

 

G、循环赛日程表 

时限:1000ms 内存限制:10000K  总时限:3000ms

描述用分治算法生成循环赛日程表(1到2的n次方个人)
输入

一个小于等于7的正整数n

输出循环赛日程表(1到2的n次方个人)
输入样例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

 注意:OJ上最后一个元素输出后没有空格

#include <iostream>

using namespace std;

int n;
int a[129][129];

void copyto(int b_x, int b_y, int m)
{
    //cout << b_x << " " << b_y << " " << m << endl;
    int e_x = b_x + m;
    int e_y = b_y + m;
    for(int i = 0; i < m; i++)
        for(int j = 0; j < m; j++){
            a[b_x + m + i][b_y + m + j] = a[b_x + i][b_y + j];
            a[b_x + m + i][b_y + j] = a[b_x + i][b_y + j + m];
        }
}

void table(){
    int m = 1; //jian ju
    int k = 1;  //ci shu
    for(int i = 1; i <= n; i++) k *= 2;
    for(int i = 1; i <= k; i++) a[1][i] = i;

    for(int s = 1; s <= n; s++){ // lie shang ci shu
        k /= 2; // hang shang ci shu
        m *= 2; //jian ju
        for(int i = 1; i <= k; i++)
            copyto(1, (i - 1) * m + 1, m / 2);
    }

}

void output(){
    int k = 1;
    for(int i = 1; i <= n; i++) k *= 2;
    for(int i = 1; i <= k; i++)
    {
        for(int j = 1; j <= k - 1; j++)
            cout << a[i][j] << " ";
        cout << a[i][k];
        cout << endl;
    }
}

int main(){
    cin >> n;
    table();
    output();
    return 0;
}

 

H、求第k小数

时限:1000ms 内存限制:10000K  总时限:3000ms

描述求第k小数
输入先输入一个小于10000的正整数n,再输入n个整数,最后输入一个小于等于n的正整数k,
输出

输出其中第k小的数。

输入样例5
2 98 34512 8492 1000
2
输出样例98

有点懒,直接使用上面的快排了;

其实可以在每次快排结束后判断枢轴的位置,与k进行比较,从而舍弃对另一部分的排序,而且元素所在部分也并不需要排序(无需对子序列递归),只需确定枢轴所在位置即可;但是这也要记录再上一次快排的结果以便确定所求数的所在范围。

#include <iostream>

using namespace std;

int a[10005];
int n;
int k;

void getdata(){
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> a[i];
    cin >> k;
}

void output()
{
    cout << a[k-1] <<endl;
}

int qsort(int left, int right){
    int pivotkey = a[left];
    while(left < right){
        while((left < right) && (a[right] >= pivotkey))right--;
        a[left] = a[right];
        while((left < right) && (a[left] <= pivotkey))left++;
        a[right] = a[left];
    }
    a[left] = pivotkey;
    return left;
}

void quicksort(int left, int right){
    if(left < right){
        int p = qsort(left, right);
        quicksort(left, p - 1);
        quicksort(p + 1, right);
    }
}

int main(){
    getdata();
    quicksort(0, n-1);
    output();
}

 

 

 

 

 

 

 

 

 

 

    原文作者:递归与分治算法
    原文地址: https://blog.csdn.net/qq_41858784/article/details/82829065
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞