1 条题解

  • 0
    @ 2026-4-2 22:16:03

    题目解析

    这道题的核心在于理解原始输入的结构与拦截后的数据关系。

    原输入结构

    原始合法输入包含:

    • 第一行:两个整数 nnmm(网格大小)
    • 接下来 nn 行:每行 mm 个整数,网格内的数值

    因此总共有:

    • 22 个整数(nnmm
    • n×mn \times m 个整数(网格内的数据)

    总整数个数

    k=n×m+2k = n \times m + 2

    问题转化

    拦截者将所有这些 kk 个整数打乱顺序,全部放在一行。题目保证 nnmm 本身也在这 kk 个整数之中(即 nnmm 两个值也作为数据出现在输入流中)。

    我们已知 kk 和打乱后的整数序列,需要恢复任意一组可能的 (n,m)(n, m)

    核心关系

    k=n×m+2k = n \times m + 2 可得:

    n×m=k2n \times m = k - 2

    我们需要在打乱后的整数中,找到两个数 nnmm,使得:

    n×m=k2n \times m = k - 2

    并且 nnmm 必须在序列中出现至少一次(注意:若 n=mn = m,需要出现至少两次)。


    解法思路

    方法一:计数器法

    11. 统计每个整数 xx 在输入中出现的次数,存入数组 freq[x]22. 遍历所有可能的 nn1nk1 \leq n \leq k):

    • n×n=k2n \times n = k - 2,检查 freq[n]>=2freq[n] >= 2(因为 nn 既作为尺寸值出现,又可能在网格内重复出现,但这里至少需要出现两次)
    • 否则,若 (k2)%n==0(k-2) \% n == 0,令 m=(k2)/nm = (k-2) / n,检查 freq[n]>0freq[n] > 0freq[m]>0freq[m] > 0(且 mm[1,k][1, k] 范围内)

    33. 输出找到的第一组 (n,m)(n, m)

    时间复杂度O(klogk)O(k \log k)(排序可优化到 O(k)O(k),但这里遍历 + 取模可视为 O(k)O(k))。

    方法二:双指针法

    11. 对输入数组排序。 22. 使用双指针 l=0l = 0, r=k1r = k-1,寻找两个数 a[l]a[l]a[r]a[r] 满足 a[l]×a[r]=k2a[l] \times a[r] = k-2。 3. 注意:nnmm 都可能等于 a[l]a[l]a[r]a[r],双指针遍历自然能处理相等情况。

    这种方法不需要额外处理 n=mn=m 的边界情况。


    代码解析(计数器法)

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        
        while (t--) {
            int k;
            cin >> k;
            
            vector<int> freq(k + 1, 0);
            
            for (int i = 0; i < k; i++) {
                int x;
                cin >> x;
                freq[x]++;
            }
            
            pair<int, int> solution = {-1, -1};
            
            // 遍历可能的 n
            for (int i = 1; i <= k; i++) {
                // 情况1: n == m
                if (i * i == k - 2) {
                    if (freq[i] > 1) {
                        solution = {i, i};
                    }
                } 
                // 情况2: n != m
                else if ((k - 2) % i == 0) {
                    int j = (k - 2) / i;
                    if (j >= 1 && j <= k && freq[i] > 0 && freq[j] > 0) {
                        solution = {i, j};
                    }
                }
            }
            
            cout << solution.first << " " << solution.second << endl;
        }
        
        return 0;
    }
    

    关键细节

    • 数组大小设为 k+1k+1 是因为输入值 aika_i \leq k
    • 检查 j1j \geq 1jkj \leq k 防止越界。
    • 如果 i×i=k2i \times i = k-2,必须保证 freq[i]>1freq[i] > 1,因为 nnmm 都需要在序列中出现(至少两个 ii)。
    • iji \neq j 时,只需 freq[i]>0freq[i] > 0freq[j]>0freq[j] > 0

    复杂度分析

    • 时间复杂度:O(k)O(k) 遍历一次统计频率,O(k)O(k) 遍历寻找解 → O(k)O(k)
    • 空间复杂度:O(k)O(k) 存储频率数组。

    题目提到 O(klogk)O(k \log k) 是因为排序方法,但这里频率数组法更优。


    总结

    这道题的本质是已知 kk 和一组包含 nnmm 的数值,求 n×m=k2n \times m = k-2 的任意解。利用频率统计可以高效求解,注意 n=mn=m 时的出现次数要求即可。

    • 1

    信息

    ID
    6279
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    4
    已通过
    1
    上传者