``` #include<iostream> #include<vector> using namespace std; inline int put(int posbefore,int need,vector<bool> &machinelem) { int pos;int cnt=0; for(int i=posbefore;;i++) { if (i >= (int)machinelem.size()) machinelem.push_back(false); if (!machinelem[i]) cnt++; else cnt = 0; if (cnt == need) { pos= i - need + 1; break; } } for(int j=pos;j<need+pos;j++) { machinelem[j]=1; } pos+=need; return pos; } inline int maxself(vector<bool> &save) { int x=(int)save.size()-1; while(x>0&&save[x]==0) x--; return x+1; } int maxope(vector<vector<bool>> &m) { int maxnumber=0; int len=m.size(); for(int i=0;i<len;i++){ if(maxself(m[i])>maxnumber) maxnumber=maxself(m[i]); } return maxnumber; } int main() { int m,n; cin>>m>>n; vector<int> arr(m*n); for(int i=0;i<m*n;i++) { cin>>arr[i]; } vector<vector<int>> arr1(n,vector<int>(m)),arr2(n,vector<int>(m));//number,time for(int i=0;i<n;i++) { for(int j=0;j<m;j++) {cin>>arr1[i][j]; } } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) {cin>>arr2[i][j]; } } vector<int> record(n,0);//寻找次数,最大到m,记录到达该件的哪个工序 int number=0; vector<vector<bool>> machine(m,vector<bool>(100,0)); vector<int> posbefore(n,0); //记录m个机器操作过程哪里被填充 for(int i=0;i<m*n;i++) { int j=arr[i]-1; int z=record[j]; int mm=arr1[j][z]-1; posbefore[j]=put(posbefore[j],arr2[j][z],machine[mm]); record[j]++; } number=maxope(machine); cout<<number; return 0; }```洛谷作业调度,提交测评,5个过,三个wa,还有两个re signal6 找出问题
时间: 2025-04-03 17:04:44 浏览: 25
<think>嗯,用户给了一个关于洛谷作业调度问题的C++代码,说提交测评时5个过了,3个WA,还有两个RE,特别是signal 6错误。我得仔细看看代码的问题在哪里。
首先,用户代码的大致逻辑是处理作业调度,用机器来模拟处理工序。代码里用了vector来记录机器的占用情况,可能是动态扩展数组。问题可能出在数组越界或者逻辑错误。
先看put函数。这个函数的作用是在机器上找到足够的位置放置工序。循环是从posbefore开始,检查machinelem数组是否有足够的连续空闲位置(由need决定)。当找到后,标记这些位置为占用,并返回下一个可能的位置。
这里有个潜在问题:machinelem是动态扩展的,当i超过当前大小时,会push_back(false)。但是循环的条件是无限循环,直到找到足够的空间。如果初始machinelem.size()很小,可能循环次数太多导致效率问题,或者在某些情况下导致数组过大,但更可能的是逻辑错误,比如没有正确计算位置。
另外,在put函数中,找到位置后,pos被赋值为i - need + 1。这里i是当前循环的位置,当cnt等于need时,i的位置是连续的第need个位置。所以起始位置应该是i - need + 1?例如,如果need=3,i从0开始,当i=2时,cnt=3,那么起始位置是0。这可能是正确的。但要注意数组下标是否正确。
另一个问题是,在put函数中,处理完位置后,pos += need,然后返回这个值。这可能是作为下一次的posbefore参数传入。例如,下一次放置同一机器的工序时,应该从pos开始找。但如果在同一机器上多次调用put,是否会出现pos超过当前machinelem.size()的情况?比如,当machinelem已经被扩展过多次,但posbefore可能被设置为一个较大的值,导致循环中的i从较大的位置开始,而machinelem可能还没扩展到那个大小,这时候push_back(false)会增加到数组末尾,但i可能远超过当前size,导致循环无法结束?
比如,假设machinelem的当前size是100,而posbefore是200,那么在第一次循环时,i=200,判断i >= size,于是push_back(false),此时size变为101,但i还是200,再次循环,继续push_back,直到i循环到某个点,这会导致无限循环,因为每次i++,但push_back一次只能增加一个元素。这会导致程序卡死,或者当内存耗尽时崩溃,出现RE。
这就是问题所在!当posbefore的值超过当前machinelem的size时,i从posbefore开始,每次循环i++,并且如果i >= size,就push_back一个元素。但是,假设需要找到连续的need个位置,比如need=5,此时i从posbefore=1000开始,那么每次循环i会递增,而machinelem的size每次只增加1,这样要到达i=1000+5的位置可能需要成千上万次循环,导致时间超限或者内存不足,或者更可能的是,当need很大时,根本无法满足连续的need个空闲位置,导致无限循环,从而触发超时或RE。
例如,当机器mm的当前machinelem的size是100,但posbefore是200,那么在put函数中,i从200开始。第一次循环,i=200 >= 100,所以push_back,size变为101。接着i++到201,继续push_back,直到i增加到某个值,而每次只能增加一个元素。这时候,为了找到连续的need个位置,可能需要循环到i=200 + need的位置,这期间会不断push_back,导致machinelem的size变得非常大,甚至内存耗尽。或者在极端情况下,无法满足条件,导致无限循环,从而引发程序崩溃,比如signal 6(SIGABRT,可能由于内存分配失败或assert失败)。
所以,问题出在put函数中,当posbefore的值远大于当前machinelem.size()时,循环无法有效找到足够的空间,导致数组无限增长或死循环。这会导致RE或TLE。
解决办法是,当posbefore超过当前machinelem.size()时,可以直接从machinelem.size()的位置开始寻找,因为后面的位置都是未被分配的,可以直接扩展足够的空间,而不需要逐个push_back。例如,如果machinelem当前的size是s,而posbefore >= s,那么可以直接在s的位置开始,此时需要至少need个连续的空闲位置(即未被占用的位置),而实际上,这些位置都不存在,所以可以直接在s的位置分配need个位置,这样就能直接找到连续的块。
因此,修改put函数,当posbefore >= machinelem.size()时,可以直接将起始位置设置为machinelem.size(),并扩展数组,填充这些位置,然后返回新的位置。
例如,在put函数中,首先检查posbefore是否超过当前size。如果是的话,那么可以直接在末尾添加need个元素,标记为占用,并返回machinelem.size() + need的位置。或者,寻找的位置可以直接在末尾,无需遍历之前的元素,因为那些位置都不存在,所以都是可用的。
这样就能避免从posbefore开始逐个检查,导致大量无效的循环和数组扩展。
另外,在put函数中,循环开始前,可能需要先处理posbefore的值是否合理。例如,如果posbefore大于等于当前machinelem.size(),那么从machinelem.size()开始找,因为前面的位置不存在,都是false吗?或者machinelem中被push_back的位置初始化为false?是的,因为push_back(false)会添加一个false元素,所以当i >= size时,添加false,也就是空闲的。
所以,当posbefore很大时,每次循环i从posbefore开始,检查i是否>=size,然后添加一个false,i继续增加,每次添加一个元素。这样,当need很大时,比如need=1e5,那么循环会执行1e5次,每次添加一个元素,这会导致时间复杂度过高,导致TLE或RE(内存不足)。
因此,正确的做法应该是,当posbefore >= current_size时,直接在current_size的位置开始分配,因为从current_size到posbefore+need的位置都是可以连续分配的,无需逐个检查。因为这些位置之前不存在,所以都是false,可以组成连续的need个位置。
因此,在put函数中,可以首先计算当前size,如果posbefore >= size,那么直接扩展数组到size + need,将这些位置标记为占用,然后返回新的posbefore + need。这样就避免了逐个添加的问题。
所以修改put函数:
int put(int posbefore, int need, vector<bool> &machinelem) {
int current_size = machinelem.size();
if (posbefore >= current_size) {
// 可以直接在末尾添加need个元素,并标记为占用
int start_pos = current_size;
machinelem.resize(current_size + need, true); // 这里可能需要用insert或循环push_back?或者直接resize并填充为true?
// 因为原来的元素是到current_size-1,现在扩展到current_size + need -1
// 但是vector<bool>的resize可能会有问题,因为需要将新添加的元素初始化为true?
for (int j = start_pos; j < start_pos + need; ++j) {
machinelem.push_back(true); // 或者直接resize并填充?
}
return start_pos + need;
} else {
// 原来的逻辑,遍历找到足够的位置
// 但可能需要优化,避免逐个扩展
// ...
}
}
不过这可能有问题,因为原来的逻辑是在machinelem数组中寻找连续的空闲位置。当posbefore在数组范围内时,需要继续原来的逻辑;而当posbefore超过数组长度时,后面的位置都是未分配的,可以视为连续的false,所以直接分配即可。但原逻辑中的machinelem在i>=size时会被push_back(false),所以当posbefore >= size时,后面的位置都是false,所以可以计算是否有足够的空间。
例如,当posbefore >= size,那么从posbefore开始的区域都是不存在的,所以需要扩展到至少posbefore + need的位置,然后检查是否有足够的连续false。但这样的扩展会导致大量的push_back,效率低下。
另一种方法是,当posbefore >= size,那么连续的可用空间的大小是无限的(因为未被分配的位置视为空闲),所以可以立即在posbefore的位置开始分配,无需检查。因为此时从posbefore到posbefore + need -1的位置都是不存在的,但每次循环时,当i超过size时,会自动push_back一个false,这样连续添加need次,所以当循环到i=posbefore时,需要添加posbefore + need - size次元素。但这会导致每次添加一个元素,直到满足条件。当need很大时,这会导致超时。
因此,正确的做法是,当posbefore >= current_size时,可以直接在current_size的位置开始分配,因为之后的位置都是空闲的(未被分配),所以可以立即分配连续的need个位置,而无需检查。
例如,假设current_size是100,posbefore是200,need是50。那么从200开始的位置都是不存在的,因此可以立即分配从current_size(100)的位置开始,连续分配50个位置,这需要将machinelem扩展到100 +50 =150,并将这些位置标记为true。这样,不需要逐个检查,而是直接扩展。
所以修改put函数:
在put函数开始时,处理两种情况:
1. posbefore的位置在现有数组范围内:需要遍历寻找足够的连续空间。
2. posbefore的位置在数组范围外:直接扩展数组到posbefore + need,并填充这些位置为true。或者更高效的是,直接计算从当前数组的末尾开始分配足够的空间。
例如:
int put(int posbefore, int need, vector<bool> &machinelem) {
int start_pos;
if (posbefore >= (int)machinelem.size()) {
// 当前数组末尾到posbefore之前的位置都是空闲的,可以分配在数组末尾
start_pos = machinelem.size();
machinelem.resize(start_pos + need, true);
return start_pos + need;
}
// 否则,继续原来的逻辑,寻找连续空间
int cnt = 0;
int i;
for (i = posbefore; ; i++) {
if (i >= (int)machinelem.size()) {
machinelem.push_back(false);
}
if (!machinelem[i]) {
cnt++;
} else {
cnt = 0;
}
if (cnt == need) {
start_pos = i - need + 1;
break;
}
}
// 标记这些位置为true
for (int j = start_pos; j < start_pos + need; j++) {
machinelem[j] = true;
}
return start_pos + need;
}
这样,当posbefore大于等于当前数组大小时,直接在数组末尾扩展need个位置,并将它们标记为占用(true)。这样避免了逐个push_back,提高效率,并解决了可能的死循环问题。
这可能就是导致RE的原因,因为当posbefore很大时,原代码中的循环会不断push_back,导致数组过大或者超时,而修改后直接扩展数组到所需的大小。
另一个可能的问题是,在put函数中,当找到start_pos后,标记这些位置为true时,是否越界。例如,原代码中的循环可能i已经超过了数组的大小,导致machinelem[j]访问越界。例如,当machinelem的size是s,而j的范围是start_pos到start_pos+need-1,其中start_pos+need-1可能大于等于s。这会导致访问越界。
例如,原代码中的处理是,在循环中,当i >= size时,push_back(false),所以当找到足够的位置时,数组的大小可能已经被扩展到足够大。例如,当start_pos是i - need +1,而i是某个位置,那么start_pos + need -1 = i,此时数组的大小至少是i+1。所以在标记为true时,数组的大小是足够的,不会越界。
所以这部分可能没问题。
现在看主函数中的处理。每次处理一个工序,调用put函数,将posbefore[j]更新为返回的新位置。这里的j是工件的编号,每个工件可能有多个工序,每个工序对应不同的机器。因此,每个机器有一个machine数组,记录时间片的占用情况。
另一个可能的问题是在maxself函数中,计算某个机器的最大时间点。例如,maxself遍历数组,找到最后一个true的位置,返回x+1。假设数组是[1,0,0],则x=0,返回1。但如果数组全为false,返回0+1=1?这可能有问题,因为如果机器从未被使用过,maxself应返回0,但此时返回的是x+1,其中x初始化为数组大小-1,循环后可能为-1,但循环条件是x>0,所以当x=0时,循环不执行,返回0+1=1?比如,如果save.size()为0,则maxself返回0+1=1?这显然不正确。
比如,machinelem可能是一个空的vector,当从未被处理过时,maxself的循环初始x是save.size()-1 = -1,然后循环条件x>0不成立,所以直接返回x+1=0。这正确吗?
例如,当save.size()为0时,x初始化为-1,循环条件x>0为false,返回x+1=0。这正确,因为机器未被使用过,最大时间为0。
当save.size()为100,且所有元素为false,那么循环中x从99开始,检查save[x]是否为0,是的,所以x递减到0,此时save[0]是0,循环停止,返回x+1=1。但此时机器的时间应该是0,因为没有占用过任何时间。所以maxself函数在这种情况下会错误地返回1,而实际应为0。
这就是问题所在!当机器从未被使用时,maxself返回0,但当机器被分配过时间片但又被释放(比如所有位置都是false)时,maxself返回的是最大的索引+1,这可能不正确。例如,假设machinelem的size是100,但所有元素都是false,那么maxself会返回100,而实际上机器并没有被占用,应该返回0。
这会导致在计算maxope时得到错误的最大值,从而输出错误的总时间。
因此,maxself函数需要修改,正确计算最大的被占用的时间片的结束位置。
正确的逻辑应该是,遍历数组,找到最后一个true的位置,然后返回该位置+1。如果没有true,返回0。
修改maxself函数:
int maxself(vector<bool> &save) {
int x = (int)save.size() - 1;
while (x >= 0 && save[x] == false) {
x--;
}
return x + 1;
}
原代码中的循环条件是x>0,所以当x=0时不会检查save[0],导致可能漏掉最后一个元素。修改为x >=0,这样会检查所有元素。例如,如果save[0]是true,x会停在0,返回1。如果所有元素都是false,x变为-1,返回0。
这样,maxself函数就能正确返回最大的时间点。
这可能解释了部分WA的情况,因为原代码在处理全false的机器时返回错误的值,导致总时间计算错误。
总结问题:
1. put函数在posbefore超过当前数组大小时,导致大量push_back,可能引发RE或TLE。
2. maxself函数中的循环条件错误,导致在某些情况下返回错误的最大时间点。
修改这两个部分应该可以解决问题。
现在,针对原代码的修改:
首先修改put函数,处理posbefore >= current_size的情况,直接扩展数组:
在put函数开始处:
int put(int posbefore, int need, vector<bool> &machinelem) {
int current_size = machinelem.size();
if (posbefore >= current_size) {
// 直接添加到末尾
int start_pos = current_size;
machinelem.resize(current_size + need, false); // 先扩展到足够大小
// 然后将这些位置标记为true
for (int j = start_pos; j < start_pos + need; ++j) {
machinelem[j] = true;
}
return start_pos + need;
}
// 否则继续原逻辑
// ...原循环代码
}
但原代码中的处理是,当i >= size时,push_back(false),所以在原循环中,当posbefore < current_size时,循环可能会扩展数组。这部分逻辑是否正确?
假设原来的逻辑是正确的,但当posbefore >= current_size时,直接处理,可以避免大量循环。
其次,修改maxself函数:
inline int maxself(vector<bool> &save)
{
int x = (int)save.size() - 1;
while (x >= 0 && !save[x]) // save[x] == false
x--;
return x + 1;
}
这样,无论数组是否全为false,都能正确返回0。
这两个修改应该能解决RE和WA的问题。
测试用例:
例如,当机器从未被使用过,maxself返回0。当机器在位置0被占用,返回1。当机器在位置5被占用,返回6。
对于put函数,当posbefore为1000,need为5,current_size为10,那么直接扩展数组到10+5=15,并填充位置10-14为true,返回15。这样避免了逐个push_back,提高效率。
另一个可能的错误是在主函数中,当处理工序时,是否正确地引用了arr1和arr2的索引?例如,arr1和arr2的定义是n行m列,而循环中的i和j是否正确?
原代码中的arr1和arr2的定义:
vector<vector<int>> arr1(n,vector<int>(m)), arr2(n,vector<int>(m));
然后输入:
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{cin>>arr1[i][j];
}
}
同样的arr2。所以arr1[j][z]中的j是工件编号,z是工序编号。每个工件有m个工序?这可能有问题。例如,题目中的每个工件可能有多个工序,但这里的m是否是机器数量?或者这里的n是工件数量,m是每个工件的工序数?
根据变量命名,m和n可能分别代表机器数和工件数?或者反过来?需要看题目要求。但原代码中的输入方式可能有问题。例如,假设题目中的输入顺序是每个工件的工序数和机器分配,但原代码中arr1和arr2是n行m列,其中n是工件数,m是工序数。这可能导致当工序数不等于m时出现错误,但根据问题中的代码,用户可能假设每个工件有m个工序。这可能与题目的实际要求不符,导致WA。例如,题目中的每个工件可能有不同的工序数,但原代码假设每个工件有m个工序,这可能错误。
但用户提到代码在洛谷提交时有部分通过,所以可能问题中的每个工件确实有m个工序。不过这可能因题而异,但原代码中的错误可能不在于此。
综上,主要问题在于put函数和maxself函数。修改这两个部分应该能解决大部分问题。</think>该代码存在两个关键问题,导致部分测试用例出现错误(WA)和运行时错误(RE):
---
### 一、`put` 函数中的 **数组越界与死循环风险**
**问题分析**:
1. 当 `posbefore` 远大于当前 `machinelem` 数组长度时,循环会逐个 `push_back`,导致数组膨胀效率极低。
2. 若 `need` 较大,可能导致 **超时(TLE)** 或 **内存耗尽(RE)**。
**修复方案**:
```cpp
inline int put(int posbefore, int need, vector<bool> &machinelem) {
int current_size = machinelem.size();
if (posbefore >= current_size) { // 直接处理尾部追加
int start = current_size;
machinelem.resize(start + need, false); // 预分配空间
fill(machinelem.begin() + start, machinelem.begin() + start + need, true);
return start + need;
}
// 原逻辑处理已有范围内的查找
int cnt = 0, pos = 0;
for (int i = posbefore;; i++) {
if (i >= (int)machinelem.size()) machinelem.push_back(false);
if (!machinelem[i]) cnt++;
else cnt = 0;
if (cnt == need) {
pos = i - need + 1;
break;
}
}
fill(machinelem.begin() + pos, machinelem.begin() + pos + need, true);
return pos + need;
}
```
---
### 二、`maxself` 函数中的 **逻辑错误**
**问题分析**:
1. 原代码在遍历数组时错误地使用 `x > 0` 作为条件,导致最后一个元素(`x=0`)未被检查。
2. 当机器完全未被使用时,错误返回 `1`(正确应为 `0`)。
**修复方案**:
```cpp
inline int maxself(vector<bool> &save) {
int x = (int)save.size() - 1;
while (x >= 0 && !save[x]) x--; // 修正循环条件
return x + 1; // 正确返回有效长度
}
```
---
### 其他潜在优化点
1. **输入逻辑验证**:检查输入的工件工序数是否与 `m` 匹配,避免非法访问。
2. **预分配内存**:初始化 `machine` 向量时预留合理空间,减少动态扩容开销。
---
### 修复后的完整代码
将上述修复点整合到原代码中,并验证边界条件后,应可通过全部测试用例。主要修改集中在 `put` 和 `maxself` 函数,确保数组操作安全且逻辑正确。
阅读全文