4 条题解

  • 0
    @ 2026-3-16 17:30:13

    针对“最长类斐波那契子序列”,我为你准备了这个集成化的数据生成器。它能够自动生成 10 组测试点,并根据竞赛逻辑实现了阶梯式难度,以确保暴力算法(O(N3)O(N^3))能拿部分分,而只有 O(N2)O(N^2) 算法能拿满分。

    数据生成器与标程 (C++14 标准)

    该代码运行后会直接在当前目录下生成 1.in10.out 共 20 个文件。

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <random>
    #include <unordered_map>
    #include <algorithm>
    #include <chrono>
    
    using namespace std;
    
    // ======================== 标程 (Solver) O(N^2) ========================
    // 采用哈希表优化的 DP 算法,用于生成 .out 文件
    int get_ans(int n, const vector<int>& a) {
        if (n < 3) return 0;
        unordered_map<int, int> val_to_idx;
        val_to_idx.reserve(n);
        for (int i = 0; i < n; ++i) val_to_idx[a[i]] = i;
    
        // dp[j][i] 表示以 a[j], a[i] 结尾的序列长度
        // 使用一维数组模拟二维以提升内存连续性性能
        vector<int> dp(n * n, 0);
        int max_len = 0;
    
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                int target = a[i] - a[j];
                // 因为数组严格递增,target 必须小于 a[j] 才能保证其索引 k < j
                if (target < a[j] && val_to_idx.count(target)) {
                    int k = val_to_idx[target];
                    dp[j * n + i] = dp[k * n + j] + 1;
                    if (dp[j * n + i] == 1) dp[j * n + i] = 3; // 初始长度应为 3 (k, j, i)
                    max_len = max(max_len, dp[j * n + i]);
                }
            }
        }
        return max_len >= 3 ? max_len : 0;
    }
    
    // ======================== 构造器 (Generator) ========================
    void generate_test_case(int tid) {
        string in_name = to_string(tid) + ".in";
        string out_name = to_string(tid) + ".out";
        ofstream fin(in_name);
        
        mt19937 rng(tid + chrono::steady_clock::now().time_since_epoch().count());
        
        int n;
        vector<int> a;
        const int INF = 1000000000;
    
        // --- 难度阶梯设计 ---
        if (tid <= 3) n = 150;        // 暴力 O(N^3) 可过
        else if (tid <= 6) n = 800;   // O(N^2 log N) 勉强过,O(N^3) 必挂
        else n = 2500;                // 只有最优 $O(N^2)$ 可过
    
        // --- 数据分布构造 ---
        if (tid == 2) { 
            // 边界点:构造完全没有解的情况 (等比数列)
            for(int i=0; i<n; ++i) a.push_back(100 + i * 1000000);
        } 
        else if (tid == 3) {
            // 构造:包含一个极长的斐波那契序列
            vector<long long> fib = {1, 2};
            while(fib.back() < INF) {
                fib.push_back(fib[fib.size()-1] + fib[fib.size()-2]);
            }
            for(auto v : fib) if(v <= INF) a.push_back((int)v);
            while((int)a.size() < n) a.push_back(rng() % INF);
        }
        else {
            // 普通构造:随机增加的序列
            // 为了增加成链几率,随机选取两个数作为起点构造若干短链
            for(int i=0; i < n/10; ++i) {
                long long v1 = rng() % 1000 + 1;
                long long v2 = v1 + rng() % 1000 + 1;
                a.push_back((int)v1); a.push_back((int)v2);
                while(v1 + v2 <= INF) {
                    long long v3 = v1 + v2;
                    a.push_back((int)v3);
                    v1 = v2; v2 = v3;
                    if(rng() % 10 == 0) break; // 随机断裂
                }
            }
            while((int)a.size() < n) a.push_back(rng() % INF);
        }
    
        // 必须保证严格递增且不重复
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
        n = a.size();
    
        // 写入文件
        fin << n << "\n";
        for (int i = 0; i < n; ++i) fin << a[i] << (i == n - 1 ? "" : " ");
        fin.close();
    
        // 计算标准答案
        ofstream fout(out_name);
        fout << get_ans(n, a) << endl;
        fout.close();
    
        cout << "Test Case " << tid << " generated: N=" << n << endl;
    }
    
    int main() {
        cout << "Generating 10 test cases for Longest Fibonacci-Like Subsequence..." << endl;
        for (int i = 1; i <= 10; ++i) {
            generate_test_case(i);
        }
        cout << "Done." << endl;
        return 0;
    }
    

    数据分布与复杂度分析

    测试点 NN 范围 构造特征 区分算法
    1 N150N \approx 150 随机分布 基础 O(N3)O(N^3) 暴力能拿分
    2 无合法子序列 测试对 max_len < 3 返回 0 的处理
    3 包含标准 FnF_n 验证最长路径检索能力
    4-6 N800N \approx 800 混合随机链 O(N3)O(N^3) 超时,O(N2logN)O(N^2 \log N) 刚好 AC
    7-10 N=2500N = 2500 大规模稀疏/稠密链 必须使用 O(N2)O(N^2) (哈希或双指针)

    关键设计点说明

    1. 区分算法复杂度
      • N=2500N=2500 是关键。O(N2)O(N^2) 的操作次数约为 6.25×1066.25 \times 10^6,在 C++ 中配合哈希表查找约耗时 0.1s0.3s0.1s \sim 0.3s
      • O(N3)O(N^3) 的操作次数约为 1.5×10101.5 \times 10^{10},在 1 秒时限内绝对无法通过。
    2. 文件大小控制
      • 即使是 N=2500N=2500 的测试点,每个整数约 10 字节,文件大小约 25KB25KB。10 组总大小约 250KB250KB,远低于 2MB2MB 的限制。
    3. 安全性与异常
      • 严格递增:使用了 sort + unique,满足题目“严格递增”的要求。
      • 溢出处理:在生成 Fibonacci 链时使用了 long long 判断,防止计算 A[k]+A[j]A[k]+A[j] 时超过 int 上限导致死循环。
      • 非递归实现:标程和生成器均采用循环结构,避免深度递归导致的爆栈。
    4. 随机性
      • 使用 mt19937 配合时间戳种子,保证每次生成的数据分布均匀且不重复。

    使用方法:

    1. 将代码保存为 gen.cpp
    2. 编译运行:g++ gen.cpp -o gen -O3 -std=c++14
    3. 执行 ./gen,即可在文件夹内看到 20 个标准的测试文件。

    最长类斐波那契子序列 (Longest Fibonacci-Like Subsequence)

    信息

    ID
    19492
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者