题目背景
某涉密单位下发了某种票据,并要在年终全部收回。
题目描述
每张票据有唯一的 ID 号,全年所有票据的 ID 号是连续的,但 ID 的开始数码是随机选定的。因为工作人员疏忽,在录入 ID 号的时候发生了一处错误,造成了某个 ID 断号,另外一个 ID 重号。
你的任务是通过编程,找出断号的 ID 和重号的 ID。
数据保证断号不可能发生在最大和最小号。
输入格式
一个整数N(N<100) 表示后面数据行数,接着读入 N 行数据,每行数据长度不等,是用空格分开的若干个(不大于 100100 个)正整数(不大于 105105),每个整数代表一个 ID 号。
输出格式
要求程序首先输入要求程序输出 11 行,含两个整数 m,n,用空格分隔,其中,m 表示断号 ID,n 表示重号 ID。
输入输出样例
输入 | 输出 |
2 5 6 8 11 9 #1 10 12 9 #2 | 7 9 |
6 164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196 #1 172 189 127 107 112 192 103 131 133 169 158 #2 128 102 110 148 139 157 140 195 197 #3 185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190 #4 149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188 #5 113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119 #6 | 105 120 |
PS:#后面的数字是行数的意思
代码
#include<stdio.h>
#include<iostream>
#define MAX 100
using namespace std;
int main(){
int length;
int p,q;
int n,m=0;
cin>>n;
int array[MAX];
for(int i=0;i<MAX;i++){
cin>>array[i];
char c;
cin.get(c);
if(c=='\n'){
m++;
}
if(m>n-1){
length=i+1;
break;
}
}
for(int i=0;i<length-1;i++){
for(int j=0;j<length-1-i;j++){
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
int j=0;
int k=j+array[0];
for(j=0;j<length;j++){
if(array[j]!=k){
p=array[j]-1;
k++;
}
k++;
if(array[j]==array[j+1]){
q=array[j];
k--;
}
}
cout<<p<<' '<<q;
return 0;
}
思路
看到这道题目,我首先想到将输入的数据存放到一个int型的数组中,然后再遍历找出有问题的数据。
代码解析(3步)
1、输入数据并合理退出循环
2、为数组排序,方便查找
3、遍历数组找出不合格的数据
参数 | 含义 |
length | 在输入的数据存到数组后,记录数组的长度 |
p | 记录断号ID的位置 |
q | 记录重复ID的位置 |
n | n行数据 |
m | 记录人为输入回车的数量 |
array[MAX] | 存放数据的数组 |
1、输入数据并合理退出循环
for(int i=0;i<MAX;i++){
cin>>array[i];
char c;
cin.get(c);
if(c=='\n'){
m++;
}
if(m>n-1){
length=i+1;
break;
}
}
先循环输入数据,但是这里我碰到了一个问题,就是数组的大小是固定的,为MAX,但是我们输入数据时的数量是可变的,还有题目的要求,输入N行数据。那么,我们如何成功输入数据并让循环顺利退出呢?
上述代码定义了一个char类型的临时变量c,用cin.get()函数来接收之前输入数据时残留在缓存中的换行符(当用cin>>读取数据时候,最后的换行符会残留在cin缓存中)。然后再令m++,记录换行符的数量,当m超过我们想要输入的n行数据后,就利用break语句退出循环,并且让length记录i的值,方便后面循环使用。
问题
1、为什么是m>n-1而不是m>n?
假设输入两行数据时,此时n=2,数据如下。
2 5 6 8 11 9 10 12 9 |
总共按下三个换行符,但是第一个换行符是在循环外完成的,不做考虑。
按下另外两个换行符要执行if语句,m=2,2要大于一个数,所以右边必须是1,n-1就是1了。
PS:一定要设置m的初始值为0,要不然编译器
2、为什么是length=i+1而不是length=i?
当缓存池中最后一个数据被编译器读取时,i还没有进行自加,当循环结束时,i才会自加,编译器读取数据时会比i++慢一步。如图。
输入的数据还是题目中m=2时的数据。可以看到,当if语句准备要break(此时m=2,n=2,下一步编译器就会执行n-1,跳出循环),array数组中已经存了8个数据了,但是此时i的值还是7,那么i值什么时候自增呢?
答案当然是不会了,因为有break语句,程序会跳出循环。没有break语句时,代码中黄色的条条就会跳到循环的开头,就是for(int i=0;i<MAX;i++)这一句,因为i++不就在这里嘛。
综上所述,length时要等于i+1才合理的。
2、为数组排序,方便查找
for(int i=0;i<length-1;i++){
for(int j=0;j<length-1-i;j++){
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
一个简单的冒泡排序。
PS:此时数组长度知道了,用不到MAX了,用length就好。
3、遍历数组找出不合格的数据
int j=0;
int k=j+array[0];
for(j=0;j<length;j++){
if(array[j]!=k){
p=array[j]-1;
k++;
}
k++;
if(array[j]==array[j+1]){
q=array[j];
k--;
}
}
请先忽略j和k这两个变量。
for循环遍历数组,两个if语句找出不合格的数据。
第一个if语句
题目要求是找出中断的一个数字,如图。
0 | 1 | 2 | 3 | 4 | 5 |
3 | 4 | 5 | 7 | 8 | 9 |
上面一行是j的值(也是数组下标),下面一行是数据。
因为下面一行数据也是递增的,虽然有断层。让下标加上数据的第一个数,这个值就是数据了(断层前的数据),判断断层的条件就出来了,就是当j+array[0]不等于array[i]时,就记录下这个断层,同时要在if语句中让j+array[0]自加,因为array[i]已经断层了一次了,已经跳过一个数了,在表中就是直接从5到7了,为了不影响后面的数据,我们就修好断层,让if语句另外一边(j+array[0])也自加一次。
记得要让j+array[0]数据不能收到循环的影响,所以要定义在循环的外面(因为j在循环里会自加),用一个变量k就行,让k代替j+array[0]在循环里工作。同时让k和j保持同步的状态,在if语句外自增就行,就是k++。
用变量p来记录断层,在表格中3和对应的7属于断层,所以让p=6就行,代码就是p=array[j]-1;
第二个if语句
当数组中相邻数据相等时就用q记录,记得要让k--。如图
0 | 1 | 2 | 3 | 4 | 5 |
3 | 4 | 4 | 5 | 6 | 7 |
数字4重复了,此时第一个if语句中,array[i]就会比另外一边慢了一步,所以要让k这个变量等一下它,所以要k自减一下;
小结
此文章是我发布的第二篇文章,还有很多不成熟的地方,代码有错也欢迎大家提出来或者私信我。代码也不一定是最优质的,主要是想记录下自己的思路和大家分享一下。喜欢我的文章就点个小小的爱心吧!