题解:洛谷 P2035 iCow
【题目来源】
洛谷:P2035 [USACO08JAN] iCow B - 洛谷
【题目描述】
被无止境的农活压榨得筋疲力尽后,FarmerJohn 打算用他在 MP3 播放器市场新买的 iCow 来听些音乐,放松一下。FJ 的 iCow 里存了 \(N(1 \le N \le 1{,}000)\) 首曲子,按 \(1..N\) 依次编号。至于曲子播放的顺序,则是按一个 FarmerJohn 自己设计的算法来决定:
- 第 \(i\) 首曲子有一个初始权值 \(R_i\ (1 \le R_i \le 10{,}000)\)。
- 当一首曲子播放完毕,接下来播放的将是所有曲子中权值最大的那首(如果有两首或多首曲子的权值相同,那么这些曲子中编号最小的那首会被选中)。
- 一首曲子在播放结束后,它的权值会被平均地分给其他 \(N-1\) 首曲子,它本身的权值清零。
- 如果一首曲子的权值无法被平均分配(也就是说,无法被 \(N-1\) 整除),那么被 \(N-1\) 除的余数部分将会以 \(1\) 为单位,顺次分配给排名靠前的曲子(也就是说,顺序为曲目 \(1\) 、曲目 $2 \cdots $ 依次下去。当然,刚播放过的那首曲子需要被跳过),直到多出的部分被分配完。
在选定的下一首曲子播放完毕后,这个算法再次被执行,调整曲子的权值,并选出再接下来播放的曲目。
请你计算一下,按 FJ 的算法,最先播放的 \(T\ (1 \le T \le 1000)\) 首曲子分别是哪些。
【输入】
第 \(1\) 行有两个用空格隔开的整数:\(N\) 和 \(T\)。
第 \(2\) 至第 \(N+1\) 行,第 \(i+1\) 行为 \(1\) 个整数 \(R_i\)。
【输出】
输出共 \(T\) 行,第 \(i\) 行输出一个整数,表示 iCow 播放的第 \(i\) 首曲子。
【输入样例】
3 4
10
8
11
【输出样例】
3
1
2
3
【核心思想】
-
问题分析:给定 \(N\) 首曲子,每首曲子有初始权值 \(R_i\),需要按 FJ 的算法依次选出 \(T\) 首要播放的曲子。每次选曲规则为:选择权值最大的曲子(权值相同时选编号最小的),播放后将其权值清零并平均分配给其他 \(N-1\) 首曲子(余数按编号顺序分配给靠前的曲子)。这是一个模拟问题,关键在于严格按照题目规则逐步模拟 \(T\) 次选曲过程。
-
算法选择:
- 直接模拟:每次遍历 \(N\) 首曲子找到权值最大者,然后按规则重新分配权值
- 编号优先处理:权值相同时选编号最小的,通过在遍历中严格使用
>比较(而非>=)自然实现
-
关键步骤:
- 初始化:读取 \(N\)(曲子数量)、\(T\)(播放次数),以及 \(R[1..N]\)(初始权值),同时记录每首曲子的编号
- 外层循环(执行 \(T\) 次):
- 选曲:遍历 \(i\) 从 \(1\) 到 \(N\),找到满足 \(R_i > \text{当前最大值}\) 的曲子,记录其编号 \(\text{maxnum}\)
- 输出:输出 \(\text{maxnum}\) 作为本次播放的曲子编号
- 计算分配值:
- \(\text{avg} = R_{\text{maxnum}} / (N-1)\):整除部分,每首其他曲子分配 \(\text{avg}\)
- \(\text{ys} = R_{\text{maxnum}} \% (N-1)\):余数部分,需要额外分配
- 清零:\(R_{\text{maxnum}} = 0\)
- 分配整除部分:遍历 \(i\) 从 \(1\) 到 \(N\),跳过 \(\text{maxnum}\),其余每首曲子 \(R_i += \text{avg}\)
- 分配余数部分:遍历 \(i\) 从 \(1\) 到 \(N\),跳过 \(\text{maxnum}\),每首曲子 \(R_i += 1\),同时 \(\text{ys}--\),直到 \(\text{ys} = 0\)
- 重复上述过程 \(T\) 次
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times N)\),每次选曲需要 \(O(N)\) 遍历找最大值,分配权值需要 \(O(N)\),共执行 \(T\) 次
- 空间复杂度:\(O(N)\),存储 \(N\) 首曲子的权值和编号
-
模拟的核心思想:
- 严格按规则执行:模拟题的核心是准确理解题意,将文字描述转化为代码逻辑,不省略任何细节
- 编号优先的自然实现:使用
>而非>=比较,确保权值相同时编号小的优先被选中 - 余数分配的循环处理:余数按编号顺序分配,遇到刚播放过的曲子跳过,用简单循环配合
continue实现 - 数据范围可控:\(N \le 1000\),\(T \le 1000\),\(O(T \times N)\) 完全在可接受范围内,无需优化
- 适用于规则明确、步骤清晰、数据范围较小的过程模拟类问题
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, T;
struct node {
int num, r;
}a[1005];
int main()
{
cin >> n >> T; // 输入n和T
for (int i=1; i<=n; i++) { // 输入n首曲子的权值
cin >> a[i].r;
a[i].num = i; // 以及编号
}
while (T--) { // 遍历T次循环
int maxn = -1e9, maxnum=-1; // 定义最大权值和其编号
for (int i=1; i<=n; i++) { // 遍历n首曲子
if (a[i].r>maxn) { // 找到最大的权值以及对应的编号
maxn = a[i].r;
maxnum = i;
}
}
cout << maxnum << endl; // 输出最大权值的编号
int avg = a[maxnum].r / (n-1); // 计算整除的平均值
int ys = a[maxnum].r % (n-1); // 计算余数
a[maxnum].r = 0; // 将最大权值的曲子的权值置为0
for (int i=1; i<=n; i++) { // 遍历n首曲子,将平均值加到n-1首曲子上
if (i==maxnum) continue;
a[i].r += avg;
}
for (int i=1; i<=n; i++) { // 从第1首开始,将余数以1为单位分配到每首曲子上
if (ys==0) break;
if (i==maxnum) continue;
a[i].r += 1;
ys--; // 每分配一个,余数减1,直至为0
}
}
return 0;
}
【运行结果】
3 4
10
8
11
3
1
2
3
浙公网安备 33010602011771号