C* Online Judge

Longest Increasing Subsequence / 最长严格递增子序列

180s · 1024 MiB memory · 256 MiB scratch

Judge: stonebuddha/cstar@sha256:5dcb310423703ae4ee37e7334802f6bb7f8af079e6e3291b5ee9f0be44c6ccf8 · linux/amd64

Longest Increasing Subsequence / 最长严格递增子序列

English

Given an integer array nums of length n, prove that the supplied function length_of_lis_dp(nums, n, dp) returns the length of its longest strictly increasing subsequence. A subsequence retains the original index order; its elements need not be adjacent. Equal values cannot extend it.

The input satisfies the following constraints:

The implementation uses O(n^2) dynamic programming. It initializes dp[i] to 1, then scans every earlier index j. When nums[j] < nums[i], it takes the larger of dp[i] and dp[j] + 1. Thus each completed dp[i] describes the longest strictly increasing subsequence ending at index i. The function returns the largest completed value, leaves nums unchanged, and initializes all n workspace entries. It uses O(n) caller-provided workspace and constant additional runtime space. The C* version uses equivalent while loops and local variables that cache individual array reads. The full bound of 100000 is part of the specification even though this quadratic version can be slow on large inputs.

The fixed header defines the result through subsequences:

The contract records nums as the ghost list input. Its postcondition requires LISLength(input, result), 1 <= result <= n, ownership of the unchanged input, and ownership of a fully initialized workspace of length n. It does not require the caller to provide initial workspace values. lis_undef(dp, i, n) owns the uninitialized suffix [i, n).

Define any auxiliary predicates and prove the lemmas needed for your invariants in starter.c; the header supplies only the contract model and its dependencies.

The implementation and contract are fixed. Add loop invariants and proof code only inside starter.c; changing the implementation or specification is rejected by the judge and consumes an attempt. Keep the supplied header and contract annotations. Submit the completed starter.c; no main function or standard input/output handling is needed.

中文

给定长度为 n 的整数数组 nums,请证明给定函数 length_of_lis_dp(nums, n, dp) 返回最长严格递增子序列的长度。子序列保留原数组中的下标顺序,但元素不必连续;相等的值不能延长严格递增子序列。

输入满足以下约束:

给定实现采用 O(n^2) 动态规划。先将 dp[i] 初始化为 1,再扫描所有先前下标 j。当 nums[j] < nums[i] 时,用 dp[i] 与 dp[j] + 1 的较大值更新 dp[i]。因此,每个已完成的 dp[i] 表示以位置 i 结尾的最长严格递增子序列长度。函数返回所有已完成值中的最大值,保持 nums 不变,并初始化全部 n 个工作区元素。算法使用调用者提供的 O(n) 工作区,其余运行时额外空间为常数。C* 版本将 for 循环改写为等价的 while 循环,并使用局部变量缓存单次数组读取。规约仍保留 100000 的完整上界;该二次复杂度版本在大输入上可能运行较慢。

给定头文件通过子序列定义结果:

契约使用幽灵列表 input 记录 nums 的内容。后置条件要求 LISLength(input, result)、1 <= result <= n,保留未改变的输入数组所有权,并得到长度为 n、已完全初始化的工作数组所有权。调用者无须提供工作区初值。请在 starter.c 中自行定义不变式所需的辅助谓词并证明所需引理;头文件仅提供规约模型及其依赖定义。 lis_undef(dp, i, n) 表示未初始化后缀 [i, n) 的内存所有权。

实现和契约均为固定内容。你只能在 starter.c 中添加循环不变式和证明代码;修改实现或规约将被判定为失败,并消耗一次提交机会。请保留给定头文件以及契约标注。提交完成后的 starter.c 即可,不需要编写 main 函数或标准输入输出代码。

Examples / 示例

nums Result / 返回值 One longest subsequence / 一个最长子序列
[7] 1 [7]
[10, 9, 2, 5, 3, 7, 101, 18] 4 [2, 3, 7, 18]
[0, 1, 0, 3, 2, 3] 4 [0, 1, 2, 3]
[7, 7, 7, 7] 1 [7]
[4, 3, 2, 1] 1 [4]
[-10000, 0, 10000] 3 [-10000, 0, 10000]