NEFU-2023-算法设计与分析实验一递归与分治算法设计

目录

作者的话

题目一棋盘覆盖问题

C++版

python版

java版

题目二合并排序问题

C++版 

python版

Java版

题目三集合最大元问题

c++版

python版

java版


作者的话

大底包含了算法硬性规定的作业代码,并非最优解,仅供参考并会持续更新。勿要无脑copy,对自己负责。如果代码有误或者优化建议,直接相应博客下方评论或者qq找我如果对代码有理解不了的或者疑惑可以询问我,但是请确保你已经自己思考过或者查过搜索引擎(如果我原模原样搜到了资料的话我会锤你的hh)。一些语法和库的资料查询网站受个人风格影响,部分题目解题方式可能没按照教学要求,如有必要请自己按教学要求做一遍。如果可以的话,麻烦借鉴完以后给我博客来个三连啥的,这可能对我以后找工作或者其他博客的推广什么的有些帮助,感谢

题目一棋盘覆盖问题

在一个2k×2k 个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

题目有问题,应输入3 2 3,而不是8 2 3

在这里插入图片描述

C++版

#include <iostream>
#include<iomanip>
using namespace std;
using std::setw;

const int MAXN = 2050;
int board[MAXN][MAXN]; // 棋盘
int tile = 1; // L型骨牌编号

// 分治递归函数
void solve(int tr, int tc, int dr, int dc, int size) {
    if (size == 1) return; // 当前棋盘已经覆盖完毕
    int t = tile++; // 当前使用的L型骨牌编号
    int s = size / 2; // 棋盘大小的一半

    // 对左上角棋盘进行分治覆盖
    if (dr < tr + s && dc < tc + s)
        solve(tr, tc, dr, dc, s);
    else {
        board[tr + s - 1][tc + s - 1] = t;
        solve(tr, tc, tr + s - 1, tc + s - 1, s);
    }

    // 对右上角棋盘进行分治覆盖
    if (dr < tr + s && dc >= tc + s)
        solve(tr, tc + s, dr, dc, s);
    else {
        board[tr + s - 1][tc + s] = t;
        solve(tr, tc + s, tr + s - 1, tc + s, s);
    }

    // 对左下角棋盘进行分治覆盖
    if (dr >= tr + s && dc < tc + s)
        solve(tr + s, tc, dr, dc, s);
    else {
        board[tr + s][tc + s - 1] = t;
        solve(tr + s, tc, tr + s, tc + s - 1, s);
    }

    // 对右下角棋盘进行分治覆盖
    if (dr >= tr + s && dc >= tc + s)
        solve(tr + s, tc + s, dr, dc, s);
    else {
        board[tr + s][tc + s] = t;
        solve(tr + s, tc + s, tr + s, tc + s, s);
    }
}

int main() {
    int k, x, y;
    cin >> k >> x >> y; // 特殊方格的坐标

    // 初始化棋盘
    int size = 1 << k; // 棋盘大小为2^k
    for (int i = 0; i < size; i++)
        for (int j = 0; j < size; j++)
            board[i][j] = -1;

    // 设置特殊方格
    board[x][y] = 0;

    // 分治递归覆盖棋盘
    solve(0, 0, x, y, size);

    // 输出结果
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
           cout<<setw(3)<< borad[i][j] ;
        }
        cout << endl;
    }

    return 0;
}

python版

MAXN = 2050
board = [[-1] * MAXN for _ in range(MAXN)]  # 棋盘
tile = 1  # L型骨牌编号

# 分治递归函数
def solve(tr, tc, dr, dc, size):
    global tile
    if size == 1:
        return  # 当前棋盘已经覆盖完毕
    t = tile  # 当前使用的L型骨牌编号
    tile += 1
    s = size // 2  # 棋盘大小的一半

    # 对左上角棋盘进行分治覆盖
    if dr < tr + s and dc < tc + s:
        solve(tr, tc, dr, dc, s)
    else:
        board[tr + s - 1][tc + s - 1] = t
        solve(tr, tc, tr + s - 1, tc + s - 1, s)

    # 对右上角棋盘进行分治覆盖
    if dr < tr + s and dc >= tc + s:
        solve(tr, tc + s, dr, dc, s)
    else:
        board[tr + s - 1][tc + s] = t
        solve(tr, tc + s, tr + s - 1, tc + s, s)

    # 对左下角棋盘进行分治覆盖
    if dr >= tr + s and dc < tc + s:
        solve(tr + s, tc, dr, dc, s)
    else:
        board[tr + s][tc + s - 1] = t
        solve(tr + s, tc, tr + s, tc + s - 1, s)

    # 对右下角棋盘进行分治覆盖
    if dr >= tr + s and dc >= tc + s:
        solve(tr + s, tc + s, dr, dc, s)
    else:
        board[tr + s][tc + s] = t
        solve(tr + s, tc + s, tr + s, tc + s, s)


# 主程序
k, x, y = map(int, input().split())  # 特殊方格的坐标

# 初始化棋盘
size = 1 << k  # 棋盘大小为2^k
for i in range(size):
    for j in range(size):
        board[i][j] = -1

# 设置特殊方格
board[x][y] = 0

# 分治递归覆盖棋盘
solve(0, 0, x, y, size)

# 输出结果
for i in range(size):
    for j in range(size):
        print(board[i][j], end='\t')
    print()

java版

import java.util.Scanner;

public class Main {

    static int MAXN = 2050;
    static int[][] board = new int[MAXN][MAXN]; // 棋盘
    static int tile = 1; // L型骨牌编号

    // 分治递归函数
    public static void solve(int tr, int tc, int dr, int dc, int size) {
        if (size == 1)
            return; // 当前棋盘已经覆盖完毕
        int t = tile++; // 当前使用的L型骨牌编号
        int s = size / 2; // 棋盘大小的一半

        // 对左上角棋盘进行分治覆盖
        if (dr < tr + s && dc < tc + s)
            solve(tr, tc, dr, dc, s);
        else {
            board[tr + s - 1][tc + s - 1] = t;
            solve(tr, tc, tr + s - 1, tc + s - 1, s);
        }

        // 对右上角棋盘进行分治覆盖
        if (dr < tr + s && dc >= tc + s)
            solve(tr, tc + s, dr, dc, s);
        else {
            board[tr + s - 1][tc + s] = t;
            solve(tr, tc + s, tr + s - 1, tc + s, s);
        }

        // 对左下角棋盘进行分治覆盖
        if (dr >= tr + s && dc < tc + s)
            solve(tr + s, tc, dr, dc, s);
        else {
            board[tr + s][tc + s - 1] = t;
            solve(tr + s, tc, tr + s, tc + s - 1, s);
        }

        // 对右下角棋盘进行分治覆盖
        if (dr >= tr + s && dc >= tc + s)
            solve(tr + s, tc + s, dr, dc, s);
        else {
            board[tr + s][tc + s] = t;
            solve(tr + s, tc + s, tr + s, tc + s, s);
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int k = scanner.nextInt();
        int x = scanner.nextInt();
        int y = scanner.nextInt();

        // 初始化棋盘
        int size = 1 << k; // 棋盘大小为2^k
        for (int i = 0; i < size; i++)
            for (int j = 0; j < size; j++)
                board[i][j] = -1;

        // 设置特殊方格
        board[x][y] = 0;

        // 分治递归覆盖棋盘
        solve(0, 0, x, y, size);

        // 输出结果
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                System.out.print(board[i][j] + "\t");
            }
            System.out.println();
        }

        scanner.close();
    }
}

题目二合并排序问题

对n个元素组成的序列进行排序。

基本思想:将待排序元素分成大小大致相同的两个子集合,分别对两个集合进行排序,最终将排序好的子集合合并成所要求的排好序的集合。

在这里插入图片描述

C++版 

#include <iostream>

using namespace std;

const int N = 1e5 + 10;  // 数组的最大长度
int arr[N], tmp[N];     // 数组和临时数组

void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];

    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

int main() {
    int n;
    cin >> n;

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

    merge_sort(arr, 0, n - 1);

    for (int i = 0; i < n; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;

    return 0;
}

python版

def merge_sort(q, l, r):
    """
    归并排序函数,使用分治的思想将数组分成两个子数组进行排序,然后合并这两个子数组。
    :param q: 待排序的数组
    :param l: 数组的左边界
    :param r: 数组的右边界
    """
    if l >= r:  # 如果数组的长度小于等于 1,则不需要排序,直接返回
        return

    mid = (l + r) // 2  # 计算数组的中间位置
    merge_sort(q, l, mid)  # 递归排序左半部分
    merge_sort(q, mid + 1, r)  # 递归排序右半部分

    i, j, k = l, mid + 1, 0  # 初始化指针 i 和 j 分别指向左半部分和右半部分的开头,k 记录 tmp 数组的指针位置
    tmp = [0] * (r - l + 1)  # 临时数组 tmp,用来存储合并后的结果

    # 比较两个子数组的元素,将较小的元素放入 tmp 数组中
    while i <= mid and j <= r:
        if q[i] <= q[j]:
            tmp[k] = q[i]
            k += 1
            i += 1
        else:
            tmp[k] = q[j]
            k += 1
            j += 1

    # 将剩余的元素放入 tmp 数组中
    while i <= mid:
        tmp[k] = q[i]
        k += 1
        i += 1
    while j <= r:
        tmp[k] = q[j]
        k += 1
        j += 1

    # 将 tmp 数组的元素复制回原数组中
    for i in range(r - l + 1):
        q[l + i] = tmp[i]


if __name__ == '__main__':
    n = int(input())  # 输入数组的长度
    arr = list(map(int, input().split()))  # 输入数组的元素

    merge_sort(arr, 0, n - 1)  # 排序

    for x in arr:
        print(x, end=' ')  # 输出排序后的数组
    print()

Java版

import java.util.Scanner;

public class Main {

    // 数组的最大长度
    private static final int N = 100010;

    // 数组和临时数组
    private static int[] arr = new int[N];
    private static int[] tmp = new int[N];

    // 归并排序
    private static void mergeSort(int[] q, int l, int r) {
        // 当区间长度小于2时,退出递归
        if (l >= r) return;

        // 分治
        int mid = l + r >> 1;
        mergeSort(q, l, mid);
        mergeSort(q, mid + 1, r);

        // 合并
        int k = 0, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            if (q[i] <= q[j]) tmp[k++] = q[i++];
            else tmp[k++] = q[j++];
        }
        while (i <= mid) tmp[k++] = q[i++];
        while (j <= r) tmp[k++] = q[j++];

        // 更新原数组
        for (i = l, j = 0; i <= r; i++, j++) {
            q[i] = tmp[j];
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        // 输入数组元素
        for (int i = 0; i < n; ++i) {
            arr[i] = sc.nextInt();
        }

        // 归并排序
        mergeSort(arr, 0, n - 1);

        // 输出排序后的数组
        for (int i = 0; i < n; ++i) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

题目三集合最大元问题

在规模为n的数据元素集合中找出最大元。当n=2时,一次比较就可以找出两个数据元素的最大元和最小元。当n>2时,可以把n个数据元素分为大致相等的两半,一半有n/2个数据元素,而另一半有n/2个数据元素。 先分别找出各自组中的最大元,然后将两个最大元进行比较,就可得n个元素的最大元

c++版

#include <iostream>
using namespace std;

int findMax(int arr[], int n) {
    if (n == 1) {
        return arr[0];
    } else if (n == 2) {
        return max(arr[0], arr[1]);
    } else {
        int mid = n / 2;
        int leftMax = findMax(arr, mid);
        int rightMax = findMax(arr + mid, n - mid);
        return max(leftMax, rightMax);
    }
}

int main() {
    int n;
    cin >> n;
    int arr[n];
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    int maxNum = findMax(arr, n);
    cout << maxNum << endl;
    return 0;
}

python版

n = int(input()) # 输入数组长度
arr = list(map(int, input().split())) # 输入数组

def findMax(arr, n):
    if n == 1:
        return arr[0]
    elif n == 2:
        return max(arr[0], arr[1])
    else:
        mid = n // 2
    leftMax = findMax(arr[:mid], mid)
    rightMax = findMax(arr[mid:], n - mid)
    return max(leftMax, rightMax)

maxNum = findMax(arr, n)
print(maxNum) # 输出最大值

java版

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        int maxNum = findMax(arr, 0, n - 1);
        System.out.println(maxNum);
    }

    public static int findMax(int[] arr, int left, int right) {
        if (left == right) {
            return arr[left];
        } else {
            int mid = (left + right) / 2;
            int leftMax = findMax(arr, left, mid);
            int rightMax = findMax(arr, mid + 1, right);
            return Math.max(leftMax, rightMax);
        }
    }
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烟雨平生9527

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值