当前位置: 首页 > news >正文

团体程序设计天梯赛 L2-052 吉利矩阵 (DFS+剪枝)(Java实现)

所有元素为非负整数,且各行各列的元素和都等于 7 的 3×3 方阵称为“吉利矩阵”,因为这样的矩阵一共有 666 种。
本题就请你统计一下,把 7 换成任何一个 [2,9] 区间内的正整数 L,把矩阵阶数换成任何一个 [2,4] 区间内的正整数 N,满足条件“所有元素为非负整数,且各行各列的元素和都等于 L”的 N×N 方阵一共有多少种?

输入格式:

输入在一行中给出 2 个正整数 L 和 N,意义如题面所述。数字间以空格分隔。

输出格式:

在一行中输出满足题目要求条件的方阵的个数。

输入样例:

7 3

输出样例:

666

给定两个正整数 L 和 N,求满足以下条件的 N×N 矩阵的个数:

  • 矩阵中每个元素都是非负整数;
  • 每一行的元素之和恰好为 L;
  • 每一列的元素之和也恰好为 L。

输出满足条件的矩阵总数。

数据范围:1≤N≤9,1≤L≤9。

解题思路

直接枚举所有 N^{2} 个格子的时间复杂度过高,因此需要采用 降维搜索 策略,利用问题的对称性和约束条件减少搜索空间。

核心观察:最后一行与最后一列可由前 N−1 行/列唯一确定

我们只对矩阵的前 (N−1)×(N−1)子矩阵进行深度优先搜索(DFS)。一旦该子矩阵确定,其余部分可被唯一推导:

  • 对于第 i 行(1≤i<N),其最后一列的值为: a[i][N] = L - \sum_{j=1}^{N-1} a[i][j]

  • 对于第 j 列(1≤j<N),其最后一行的值为: a[N][j] = L - \sum_{i=1}^{N-1} a[i][j]

此时,最后一个元素 a[N][N] 可从两个方向计算:

  • 从第 N 行看:a[N][N] = L - \sum_{j=1}^{N-1} a[N][j]

  • 从第 N 列看: a[N][N] = L - \sum_{i=1}^{N-1} a[i][N]

这两个值在数学上是相等的,因为它们都等于: L \cdot (N - 1) - S 其中 S 是前 (N−1)×(N−1)(N−1)×(N−1) 子矩阵所有元素之和。

合法性条件

要使整个矩阵合法,只需满足以下两个条件:

  • 最后一列的前 N−1 个元素之和不超过 L: \sum_{i=1}^{N-1} (L - \sum_{j=1}^{N-1} a[i][j]) \leq L

  • 最后一行的前 N−1 个元素之和不超过 L: \sum_{j=1}^{N-1} (L - \sum_{i=1}^{N-1} a[i][j]) \leq L

由于这两个和在数值上恒等(都等于 L⋅(N−1)−S),因此只需检查其中一个是否 ≤L 即可。若成立,则 a[N][N]=L−(L⋅(N−1)−S)=L⋅(2−N)+S≥0,且所有行列和均为 L,矩阵合法。

算法设计

1. 搜索策略

使用 DFS 枚举前 (N−1)×(N−1)子矩阵中每个位置的取值。每个位置 a[i][j]a[i][j] 的取值范围为:[0, \min(L - \text{rowSum}[i], L - \text{colSum}[j])]

其中:

  • rowSum[i] 表示第 i 行当前已填元素之和;
  • colSum[j] 表示第 j 列当前已填元素之和。

2. 剪枝优化

  • 在枚举过程中,若某行或某列的当前和已超过 L,则剪枝。
  • 由于 N≤9,L≤9,且搜索空间仅为 (N-1)^{2},实际运行效率很高。

3. 终止条件

当 DFS 填完所有 (N−1)×(N−1) 的格子后,计算:

  • sumLastColPrefix = \sum_{i=1}^{N-1} (L - \text{rowSum}[i])
  • sumLastRowPrefix =\sum_{j=1}^{N-1} (L - \text{colSum}[j])

若满足: sumLastColPrefix \leq L \quad 且 \quad sumLastRowPrefix \leq L 则计数器加一。

4. 特判

当 N=1时,矩阵只有一个元素 a[1][1],其值必须为 L,因此答案为 1。

以下给出剪枝版本和打表版本,代码清晰易理解,希望互相学习。

Java实现:

import java.io.*;/*** 计算 N×N 非负整数矩阵的个数,满足:* - 每行元素之和为 L* - 每列元素之和为 L* * 核心思想:只搜索前 (N-1)×(N-1) 的子矩阵*          最后一行和最后一列由前 N-1 行/列自动确定*          只需检查最后一行前 N-1 列之和 ≤ L,且最后一列前 N-1 行之和 ≤ L*          由于数学恒等式,两者实际相等,因此只需检查上限即可。*/
public class Main {private static int targetSum;  //  每行/每列的目标和 Lprivate static int matrixSize; //  矩阵维度 Nprivate static int resultCount; // 合法矩阵总数// 记录前 N-1 行/列的当前部分和(索引 1 ~ N-1 使用)private static int[] rowPartialSum = new int[10]; private static int[] colPartialSum = new int[10]; public static void main(String[] args) throws IOException {// 读取输入BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String[] input = br.readLine().split(" ");targetSum = Integer.parseInt(input[0]);matrixSize = Integer.parseInt(input[1]);// 特判:如果 N == 1,则只有一种可能:[L]if (matrixSize == 1) {System.out.println(1);return;}// 初始化计数器和部分和数组resultCount = 0;for (int i = 0; i < 10; i++) {rowPartialSum[i] = 0;colPartialSum[i] = 0;}// 从子矩阵的左上角 (1,1) 开始深度优先搜索dfs(1, 1);// 输出结果System.out.println(resultCount);}/*** 深度优先搜索填充 (N-1) × (N-1) 子矩阵* * @param currentRow 当前行索引(从 1 开始)* @param currentCol 当前列索引(从 1 开始)*/private static void dfs(int currentRow, int currentCol) {// 如果当前列超出 N-1,换到下一行第一列if (currentCol > matrixSize - 1) {currentCol = 1;currentRow++;}// 如果已填完前 N-1 行,进行合法性检查if (currentRow == matrixSize) {checkAndCount();return;}// 枚举当前格子 (currentRow, currentCol) 可填的值// 不能超过所在行或所在列的剩余容量int maxAllowedValue = Math.min(targetSum - rowPartialSum[currentRow],  // 行剩余空间targetSum - colPartialSum[currentCol]   // 列剩余空间);for (int value = 0; value <= maxAllowedValue; value++) {// 做选择rowPartialSum[currentRow] += value;colPartialSum[currentCol] += value;// 递归填下一个格子dfs(currentRow, currentCol + 1);// 回溯rowPartialSum[currentRow] -= value;colPartialSum[currentCol] -= value;}}/*** 检查当前 (N-1)×(N-1) 子矩阵是否能扩展为完整合法矩阵* 条件:最后一列前 N-1 行之和 ≤ L,且最后一行前 N-1 列之和 ≤ L* 数学上这两个和相等,因此只需检查是否都不超过 L。*/private static void checkAndCount() {int sumLastColPrefix = 0; // 最后一列的前 N-1 个元素之和int sumLastRowPrefix = 0; // 最后一行的前 N-1 个元素之和// 计算最后一列前 N-1 行的值:每行缺多少才能到 Lfor (int i = 1; i < matrixSize; i++) {sumLastColPrefix += (targetSum - rowPartialSum[i]);}// 计算最后一行前 N-1 列的值:每列缺多少才能到 Lfor (int j = 1; j < matrixSize; j++) {sumLastRowPrefix += (targetSum - colPartialSum[j]);}// 只要两者都不超过 L,就能合法补全矩阵if (sumLastColPrefix <= targetSum && sumLastRowPrefix <= targetSum) {resultCount++;}}
}

打表法:

import java.io.*;
import java.util.StringTokenizer;public class Main {static class FastReader {BufferedReader br;StringTokenizer st;public FastReader() {br = new BufferedReader(new InputStreamReader(System.in));}String next() {while (st == null || !st.hasMoreElements()) {try {st = new StringTokenizer(br.readLine());} catch (IOException e) {e.printStackTrace();}}return st.nextToken();}int nextInt() {return Integer.parseInt(next());}}public static void main(String[] args) {// 预计算的表(索引从 0 开始,对应 l = 0,1,2,...,9)int[] n2 = {0, 0, 3, 4, 5, 6, 7, 8, 9, 10};int[] n3 = {0, 0, 21, 55, 120, 231, 406, 666, 1035, 1540};int[] n4 = {0, 0, 282, 2008, 10147, 40176, 132724, 381424, 981541, 2309384};FastReader fr = new FastReader();int l = fr.nextInt();int n = fr.nextInt();if (n == 2) {System.out.println(n2[l]);} else if (n == 3) {System.out.println(n3[l]);} else if (n == 4) {System.out.println(n4[l]);}}
}

http://www.dtcms.com/a/394947.html

相关文章:

  • 【LeetCode 每日一题】966. 元音拼写检查器
  • windows 服务器如何开启系统自带的ftp服务
  • 2025年面试经历
  • Linux 基础:目录结构
  • OpenLayers地图交互 -- 章节七:指针交互详解
  • Kafka实战案例一:阿里云Kafka智能设备数据实时处理系统
  • 回调函数与错误处理
  • 深入大模型-2-大模型微调之Windows10安装大语言模型Unsloth微调环境
  • openssl x509 -noout -text -in server.cert.pem输出字段详解
  • Linux 基础:Vi/Vim 编辑器
  • K8s和Service Mesh如何强化微服务治理能力
  • 知识图谱赋能自然语言处理的深层语义分析:技术、影响与前沿趋势
  • 论文笔记:How Can Recommender Systems Benefit from Large Language Models: A Survey
  • idea终端添加git-bash,支持linux的shell语法
  • MITRE ATLAS对抗威胁矩阵:守护LLM安全的中国实践指南
  • 常见的 Web 项目性能优化方法有哪些?​​也适用于首页
  • Qt QMainWindow类深度解析:主窗口框架的核心实现
  • 知识图谱对自然语言处理深层语义分析的革命性影响与启示
  • 内部标识符
  • 计算机网络2
  • 计算机视觉(opencv)实战三十二——CascadeClassifier 人脸微笑检测(摄像头)
  • MyBatis-Plus 全方位深度指南:从入门到精通
  • PyTorch 神经网络工具箱:从组件到基础工具,搭建网络的入门钥匙
  • 分布式专题——18 Zookeeper选举Leader源码剖析
  • JVM 调优在分布式场景下的特殊策略:从集群 GC 分析到 OOM 排查实战(二)
  • 基于OpenEuler部署kafka消息队列
  • Flink TCP Channel复用:NettyServer、NettyProtocol详解
  • Sass和Less的区别【前端】
  • Kotlin互斥锁Mutex协程withLock实现同步
  • Seedream 4.0 测评|AI 人生重开:从极速创作到叙事实践