1 条题解
-
0
解题思路
1. 对角线长度的分布
棋盘上共有 条不同的对角线( 的取值范围为 )。
对于第 条对角线(即 ),它的格子数量(长度)为:- 当 时,长度为 (递增);
- 当 时,长度为 (递减)。
因此长度序列为:
其中长度为 的对角线只有 一条(即 的主对角线);
其余长度 均出现 两次(对称的两条)。2. 贪心策略
为了尽量减少被占用的对角线数量,我们应尽可能将棋子 集中 在尽可能少的对角线上。
因此优先选取 容量最大 的对角线来放置棋子。贪心过程:
- 将所有对角线按长度从大到小排序(显然先取长度为 的那一条,然后取两条长度为 的,再取两条长度为 的,……)。
- 依次填充每条对角线,直到所有 个棋子都放完。
- 此时所使用的对角线数量即为最少被占用的对角线数。
3. 正确性证明
- 若存在一个最优解,其中某条较短的对角线被填满了,而某条更长的对角线尚未被完全利用,那么我们可以将棋子从短对角线移动到长对角线上(因为长对角线的剩余空位更多),这样不会增加占用对角线的数量,甚至可能减少。
- 因此存在一个最优解,它按照对角线长度从大到小的顺序依次填满。
- 贪心算法正好模拟了这个过程,所以得到最优解。
4. 算法实现
直接模拟即可:
- 初始化一个数组
len,包含所有对角线的长度:先放一个 ,然后从 向下到 ,每个 放两次。 - 将
len从大到小排序(其实已经有序)。 - 遍历
len,用当前长度l尝试放棋子:- 若 ,则放完 个棋子,答案加 ,结束。
- 否则 ,答案加 ,继续下一条对角线。
复杂度 每条对角线处理一次。
5. 边界情况
- :不需要任何棋子,占用对角线数为 。
- :必须放满整个棋盘,所有对角线都被占用(共 条),答案即为 。
示例讲解
以题目样例为例:
例2:
对角线长度:(一条长度 ,两条长度 )。
先填长度 的对角线,可放 个棋子 → 用完,占用 条对角线。输出 。例3:
先填长度 (放 个, 剩 ),再填一条长度 (放 个),占用 条对角线。输出 。例4:
填满长度 ( 个),再填两条长度 (各 个),占用 条对角线。输出 。例7:
对角线长度:,总容量 ,需要全部填满,占用 条对角线。输出 。
参考代码
#include <iostream> #include <vector> using namespace std; int solve(int n, int k) { if (k == 0) return 0; // 按长度从大到小列出所有对角线 vector<int> diag; // 先放最长的(长度 n) diag.push_back(n); // 然后放长度 n-1, n-2, ..., 1,每个长度出现两次 for (int i = n-1; i >= 1; --i) { diag.push_back(i); diag.push_back(i); } int ans = 0; for (int len : diag) { if (k <= len) { ans++; // 当前对角线能放下剩余所有棋子 break; } k -= len; // 填满这条对角线 ans++; } return ans; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, k; cin >> n >> k; cout << solve(n, k) << '\n'; } return 0; }
时间复杂度
每个测试用例 ,总复杂度 ,轻松通过.
- 1
信息
- ID
- 7232
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者