蓝桥杯——摆放汤圆

目录

题目链接:8.摆放汤圆【算法赛】 - 蓝桥云课

题目描述

数学发现——对合排列

推导过程

1. 分类讨论法

解决思路

方法与步骤

解法一:动态规划

Java写法:

C++写法:

AC情况

时间复杂度和空间复杂度

总结



题目链接:8.摆放汤圆【算法赛】 - 蓝桥云课

注:下述题目描述和示例均来自蓝桥云客

题目描述

元宵节到了,老王家的汤圆摊前热闹非凡,顾客们排着长队等着品尝美味的汤圆。老王一边忙活,一边琢磨着怎么把汤圆摆得既好看又吉利。

老王家的汤圆盘是一个正方形的盘子,上面有 n×n 个格子,整齐地排列成 n 行 n 列的方阵。为了让顾客们眼前一亮,老王决定在盘子上摆放 n 个大汤圆。

不过,老王这人有点强迫症,摆汤圆得遵守几条规矩:

  1. 同一列的格子里不能放两个汤圆,不然会挤到。
  2. 同一行的格子里也不能放两个汤圆,不然会滚走。
  3. 最重要的是,为了体现元宵节的对称美,汤圆的摆放必须关于对角线(从左上到右下的直线)对称。对角线上的格子本身就满足对称性。

        老王想了半天,也没算出来到底有多少种摆法。他挠了挠头,决定找你帮忙。现在,请你帮老王算算,满足这些条件的汤圆摆放方案数一共有多少种?由于答案可能很大,你只需要输出方案数对 10^9+7 取模后的结果即可。


数学发现——对合排列

推导过程

1. 分类讨论法

        考虑构造一个 n 元素的对合排列,我们可以按第 n 个元素是否是固定点来分类讨论:

情况 1:第 n 个元素是固定点
        此时,第 n 个元素不动,剩下的 n−1 个元素必须形成一个对合排列。因此,这种情况的方案数为:

C(n-1)

情况 2:第 n 个元素与某个其他元素 i(1≤i≤n−1)形成对换
此时,第 n 个元素与第 i 个元素互换位置,剩下的 n−2 个元素必须形成一个对合排列。

  • 选择 i 的方式有 n−1 种(因为 i 可以是 1,2,…,n−1 中的任意一个)。
  • 剩下的 n−2 个元素的对合排列数为 C(n−2)。
    所以,这种情况的方案数为:

(n-1)×C(n-2)

总方案数:将两种情况相加,得到递推公式:

C(n)=C(n-1)+(n-1)\cdot ×C(n-2)


解决思路

        汤圆摆放的问题,我们需要计算满足特定条件的排列数目。具体来说,汤圆必须满足以下条件:

  1. 每行每列恰好有一个汤圆。
  2. 摆放关于主对角线对称。

方法与步骤

  1. 问题分析

    • 每个汤圆的位置 (i, j) 必须与 (j, i) 同时存在,因此排列必须是对称的。
    • 这种排列称为对合排列(involution),即排列的平方等于恒等排列,所有循环长度为1或2。
  2. 递推关系

    • 对合排列的数目 C(n) 满足递推公式:

      C(n)=C(n-1)+(n-1)×C(n-2)

    • 初始条件:C(0) = 1C(1) = 1
  3. 动态规划优化

    • 使用变量 a 和 b 分别保存前两个值,逐步计算到 n,避免使用数组节省空间。

解法一:动态规划

Java写法:

import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        long t = scan.nextLong();
        //直接计算到n的值
        long[] dp = new long[1000001];
        // 初始化n=0,1
        dp[0] = 1;
        dp[1] = 1;

        for(int n = 2;n <= 1000000;n++){
          dp[n] = (dp[n - 1] + (n - 1)*dp[n - 2]) % 1000000007;
        }

        for(int i = 0;i < t;i++){
          int N = scan.nextInt();
          System.out.println(dp[N]);
        }

        scan.close();
    }
}

C++写法:

#include <iostream>
#include <vector>
using namespace std;

const int MOD = 1e9 + 7;
const int MAXN = 1000000;

int main() {
    int t;
    cin >> t;
    
    // 使用 vector 避免栈溢出
    vector<long long> dp(MAXN + 1, 0);
    dp[0] = 1;  // C(0) = 1
    dp[1] = 1;  // C(1) = 1

    for (int n = 2; n <= MAXN; ++n) {
        dp[n] = (dp[n-1] + (long long)(n-1) * dp[n-2]) % MOD;
    }

    while (t--) {
        int N;
        cin >> N;
        cout << dp[N] % MOD << endl;
    }

    return 0;
}

AC情况

时间复杂度和空间复杂度

  • 时间复杂度: O(t)O(t)
  • 空间复杂度: O(N)O(N)


总结

        很难,要懂数学知识或者是你能现场推算出来,抱歉,这里我没有推出来,我查资料了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WenJGo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值