洛谷-数组(1)
数组这个题单难度题型和前三个不大同的,思维方式也不一样,没做之前是真没想过可以放类似a[10000][10000]的数组占位置,所以有些清高的就试着用其他方法解决,虽然写的不是很简洁,但毕竟是自己花时间debug并且AC的答案,所以还是分享了出来。文章也给出了题解区的好题解供大家对比参考。
文章目录
P1047 [NOIP 2005 普及组] 校门外的树
题目描述
某校大门外长度为 l l l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 1 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 0 0 的位置,另一端在 l l l 的位置;数轴上的每个整数点,即 0 , 1 , 2 , … , l 0,1,2,\dots,l 0,1,2,…,l,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。
输入格式
第一行有两个整数,分别表示马路的长度 l l l 和区域的数目 m m m。
接下来 m m m 行,每行两个整数 u , v u, v u,v,表示一个区域的起始点和终止点的坐标。
输出格式
输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量。
ps
模拟留标记即可
#include<iostream>
using namespace std;
int main() {
int n, m,count=0;
cin >> n >> m;
int* arr = new int[n+1];
for (int i = 0; i <= n; i++)
arr[i] = 1;
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
for (int i = a ; i <= b; i++) {
arr[i] = 0;
//count+=1;
}
//cout << count << endl; count = 0;
}
for (int i = 0; i <= n; i++) {
if (arr[i] != 0)
count++;
}
cout << count;
delete[] arr;
return 0;
}
P2141 [NOIP 2014 普及组] 珠心算测验
题目背景
NOIP2014 普及 T1
题目描述
珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练,既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及。
某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正整数集合,集合中的数各不相同,然后要求学生回答:其中有多少个数,恰好等于集合中另外两个(不同的)数之和?
最近老师出了一些测验题,请你帮忙求出答案。
输入格式
共两行,第一行包含一个整数 n n n,表示测试题中给出的正整数个数。
第二行有 n n n 个正整数,每两个正整数之间用一个空格隔开,表示测试题中给出的正整数。
输出格式
一个整数,表示测验题答案。
输入输出样例 #1
输入 #1
4
1 2 3 4
输出 #1
2
ps:
注意是可以表示成其余两个数相加的数有多少个,比如2+3=5,1+4=5,有两种表达形式,但是都是表达5,所以count应该只算一次
#include<iostream>
using namespace std;
int main() {
int count[100] = {0},_count1=0;
int _count[100] = { 0 };
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> count[i];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
if (j!=k) {
if (j!=i&&k!=i&&(count[i] == count[j] + count[k])) {
_count[i] = 1;
//cout << "i:" << count[i] << " j:" << count[j] << " k:" << count[k]<<endl;
}
else
continue;
}
}
}
}
for (int i = 0; i < n; i++) {
if (_count[i] != 0)
_count1++;
}
cout << _count1;
return 0;
}
P1614 爱与愁的心痛
题目背景
(本道题目隐藏了两首歌名,找找看哪~~~)
《爱与愁的故事第一弹·heartache》第一章。
《我为歌狂》当中伍思凯神曲《舞月光》居然没赢给萨顶顶,爱与愁大神心痛啊~~~而且最近还有一些令人伤心的事情,都让人心痛(最近真的很烦哈)……
题目描述
最近有 n n n 个不爽的事,每句话都有一个正整数刺痛值(心理承受力极差)。爱与愁大神想知道连续 m m m 个刺痛值的和的最小值是多少,但是由于业务繁忙,爱与愁大神只好请你编个程序告诉他。
输入格式
第一行有两个用空格隔开的整数,分别代表 n n n 和 m m m。
第 2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行一个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数 a i a_i ai 代表第 i i i 件事的刺痛值 a i a_i ai。
输出格式
输出一行一个整数,表示连续 m m m 个刺痛值的和的最小值是多少。
输入输出样例 #1
输入 #1
8 3
1
4
7
3
1
2
4
3
输出 #1
6
ps:
思路:通过current存储目前的m位数相加结果,然后和min对比更改值复杂度接近n^2
代码:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int>arr;
for (int i = 0; i < n; i++)
{
int input;
cin >> input;
arr.push_back(input);
}
int current = 0,min=0,count=0;
if (n == m) {
for (int i = 0; i < n; i++)
min += arr[i];
}
else {
while (count < n - m) {
for (int i = count; i < m + count; i++) {
current += arr[i];
}
if (count == 0)min = current;
else min = current < min ? current : min;
current = 0;
count++;
}
}
cout << min;
return 0;
}
P2911 [USACO08OCT] Bovine Bones G
题目描述
Bessie 喜欢桌游和角色扮演游戏,所以她说服了 Farmer John 驾车送她去爱好商店,在那里她购买了三个用于掷骰子的骰子。这些公平的骰子分别有 S 1 S_1 S1、 S 2 S_2 S2 和 S 3 S_3 S3 个面( 2 ≤ S 1 ≤ 20 2 \leq S_1 \leq 20 2≤S1≤20; 2 ≤ S 2 ≤ 20 2 \leq S_2 \leq 20 2≤S2≤20; 2 ≤ S 3 ≤ 40 2 \leq S_3 \leq 40 2≤S3≤40)。Bessie 不断地掷骰子,试图找出哪个三个骰子的和出现得最频繁。给定每个骰子的面数,确定哪个三个骰子的和出现得最频繁。如果有多个和出现得最频繁,报告其中最小的和。
输入格式
第 1 行:三个用空格分隔的整数: S 1 S_1 S1、 S 2 S_2 S2 和 S 3 S_3 S3。
3 输出格式
第 1 行:当骰子以每种可能的组合掷出时,出现次数最多的最小整数和。
输入输出样例 #1
输入 #1
3 2 3
输出 #1
5
说明/提示
这里是所有可能的结果。
1 1 1 -> 3
1 2 1 -> 4
2 1 1 -> 4
2 2 1 -> 5
3 1 1 -> 5
3 2 1 -> 6
1 1 2 -> 4
1 2 2 -> 5
2 1 2 -> 5
2 2 2 -> 6
3 1 2 -> 6
3 2 2 -> 7
1 1 3 -> 5
1 2 3 -> 6
2 1 3 -> 6
2 2 3 -> 7
3 1 3 -> 7
3 2 3 -> 8
5 和 6 都出现得最频繁(各五次),所以答案是 5。
PS:
思路:注意到所有的可能性是可以提前预知的,都是从3-S1xS2xS3,我们就只用记录这个范围内每个数字出现的次数就可以了,复杂度最坏就是n^3,maybe数组对应的是可能的和结果,其实还使用到了索引和答案之间的关系(相差3)*
代码:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int s1,s2,s3,max=0,point=0;
cin >> s1 >> s2 >> s3;
int current = 0, always = 0;
vector<int>maybe(s1 + s2 + s3 - 2,0) ;
for (int i = 1; i <= s1; i++) {
for (int j = 1; j <= s2; j++) {
for (int k = 1; k <= s3; k++) {
int sum = 0;
sum = i + j + k;
maybe[sum-3]+=1;
}
}
}
for (int i = 0; i < s1 + s2 + s3-2; i++) {
if (i == 0) {
max = maybe[i];
point = i;
}
else {
if (maybe[i] > max) {
max = maybe[i];
point = i;
}
}
}
cout << point + 3;
return 0;
}
P1161 开灯
题目描述
在一条无限长的路上,有一排无限长的路灯,编号为 1 , 2 , 3 , 4 , … 1,2,3,4,\dots 1,2,3,4,…。
每一盏灯只有两种可能的状态,开或者关。如果按一下某一盏灯的开关,那么这盏灯的状态将发生改变。如果原来是开,将变成关。如果原来是关,将变成开。
在刚开始的时候,所有的灯都是关的。小明每次可以进行如下的操作:
指定两个数, a , t a,t a,t( a a a 为实数, t t t 为正整数)。将编号为 ⌊ a ⌋ , ⌊ 2 × a ⌋ , ⌊ 3 × a ⌋ , … , ⌊ t × a ⌋ \lfloor a\rfloor,\lfloor 2 \times a\rfloor,\lfloor3 \times a\rfloor,\dots,\lfloor t \times a\rfloor ⌊a⌋,⌊2×a⌋,⌊3×a⌋,…,⌊t×a⌋ 的灯的开关各按一次。其中 ⌊ k ⌋ \lfloor k \rfloor ⌊k⌋ 表示实数 k k k 的整数部分。
在小明进行了 n n n 次操作后,小明突然发现,这个时候只有一盏灯是开的,小明很想知道这盏灯的编号,可是这盏灯离小明太远了,小明看不清编号是多少。
幸好,小明还记得之前的 n n n 次操作。于是小明找到了你,你能帮他计算出这盏开着的灯的编号吗?
输入格式
第一行一个正整数 n n n,表示 n n n 次操作。
接下来有 n n n 行,每行两个数, a i , t i a_i,t_i ai,ti。其中 a i a_i ai 是实数,小数点后一定有 6 6 6 位, t i t_i ti 是正整数。
输出格式
仅一个正整数,那盏开着的灯的编号。
输入输出样例 #1
输入 #1
3
1.618034 13
2.618034 7
1.000000 21
输出 #1
20
PS:
思路:(floor实现高斯取整)。代码思路我的理解是消消乐,先通过sort实现快排,接着通过每一个数字(对应的位置)出现多少次就做多少次操作(就是反转布尔值的状态),当遇到不相同的时候,进入到结算环节,如果前面相同位置操作后是暗的,则给此时不同的第一个灯的位置第一次操作(点亮)
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
int main() {
vector<int>arr;
double a;
int n, t,before=0,current=0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a >> t;
for (int j = 1; j <= t; j++) {
arr.push_back(floor(a*j));
}
}
sort(arr.begin(), arr.end());
/*for (int i = 0; i < arr.size(); i++)
cout << arr[i] << endl;*/
before = arr[0];
bool status = true;
//cout << "arr[0]" << arr[0] << endl;
for (int i = 1; i < arr.size(); i++) {
current = arr[i];
if (current == before) {
status = !status;
before = current;
}
else {
if (status) {
current = before;
cout << current;
return 0;
}
else {
status = true;
before = current;
}
}
//cout << "before:" << before << " current:" << current << endl;
}
if(status)cout <<current ;//最后一个才是亮的情况
return 0;
}
通用代码
思路:空间换时间,直接给足空间对应数据序号
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2000001;
int light[MAXN];//状态表示。
int n, t;
double a;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a >> t;
for (int j = 1; j <= t; j++) {
int index = (int)(j * a);
light[index] ^= 1;//切换状态。
}
}
//找出唯一亮着的灯。
for (int i = 2; i <= MAXN; i++) {
if (light[i] == 1) {
cout << i << endl;
break;
}
}
return 0;
}
比较
-
如果 sum(t) 较小(如 1e4 或更小):
-
代码1 更省空间(~40KB vs 8MB)。
-
如果 sum(t) 较大(接近或超过 1e6):
-
代码2 更稳定,因为代码1的空间占用会增长,而代码2固定 8MB。
-
极端情况(sum(t) 极大,如 1e7):
-
代码1 可能占用 ~40MB,而代码2 仍然是 8MB,此时代码2 更优。
最终建议
-
优先考虑时间复杂度(代码2 通常更快)。
-
如果内存严格受限且 sum(t) 较小,可以选择代码1。
-
如果 sum(t) 可能很大,代码2 更稳定,不会因输入规模增长而占用过多内存。
P5731 【深基5.习6】蛇形方阵
题目描述
给出一个不大于
9
9
9 的正整数
n
n
n,输出
n
×
n
n\times n
n×n
的蛇形方阵。
从左上角填上 1 1 1 开始,顺时针方向依次填入数字,如同样例所示。注意每个数字有都会占用 3 3 3 个字符,前面使用空格补齐。
输入格式
输入一个正整数 n n n,含义如题所述。
输出格式
输出符合题目要求的蛇形矩阵。
输入输出样例 #1
输入 #1
4
输出 #1
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
说明/提示
数据保证, 1 ≤ n ≤ 9 1 \leq n \leq 9 1≤n≤9。
ps:
思路:我写代码的时候想的是按下面逻辑:
1 1 1 2
4 5 6 2
4 8 7 2
4 3 3 3
按照这个逻辑偶数输入图案都没有问题,奇数输入则会出现空心的问题(所以我手动补上),当输入是9的时候,会出现中心九宫格全为0的情况,所以我把原来的double函数的while(count<n*n)修改为更加确定的循环次数(for循环内的条件)。
解释一下n=9时候函数出错的原因:第一圈后一切正常,此时count为32,进入double函数(此时打印为8的蛇形矩阵),但是在跑到第三次的时候count变为72跳出了循环,这就是九宫格为0的原因
代码:
#include<iostream>
#include<iomanip>
using namespace std;
void snack_double(int arr[9][9], int &row, int &col,int n,int &count) {
for (int i = 0; i < n / 2;i++) {
for (int j = row; j < col - 1; j++) {
count++;
arr[row][j] = count;
}
for (int j = row; j < col - 1; j++) {
count++;
arr[j][col - 1] = count;
//cout << "count-down" << count << endl;
}
for (int j = col - 1; j > row; j--) {
count++;
arr[col - 1][j] = count;
//cout << "count-left" << count << endl;
}
for (int j = col - 1; j > row; j--) {
count++;
arr[j][row] = count;
//cout << "count-up" << count << endl;
}
row++;
col--;
}
}
void first_round(int arr[9][9], int &row, int &col, int n, int &count)
{
while (count < (n-1) * n) {
for (int j = row; j < col - 1; j++) {
count++;
arr[row][j] = count;
//cout << "count-right" << count << endl;
}
for (int j = row; j < col - 1; j++) {
count++;
arr[j][col - 1] = count;
//cout << "count-down" << count << endl;
}
for (int j = col - 1; j > row; j--) {
count++;
arr[col - 1][j] = count;
//cout << "count-left" << count << endl;
}
for (int j = col - 1; j > row; j--) {
count++;
arr[j][row] = count;
//cout << "count-up" << count << endl;
}
row++;
col--;
}
}
int main() {
int arr[9][9] = {0};
int n, count = 0; cin >> n;
int row = 0, col = n;
if (n == 1)
{
cout << setw(3)<<1;
return 0;
}
else if (n % 2 == 0) {
snack_double(arr, row, col, n, count);
}
else {
first_round(arr, row, col, n, count);
snack_double(arr, row, col,n-1, count);
arr[(n - 1) / 2][(n - 1) / 2] = n * n;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
cout <<setw(3)<< arr[i][j];
if(i!=n-1)cout << endl;
}
return 0;
}
其他参考题解(代码更干净)
#include<iostream>
#include<cstdio>
using namespace std;
int a[20][20];
int main()
{
int n;
int now = 2, x = 1, y = 1;
a[1][1] = 1;
cin >> n;
while (now <= n * n) {
while (y + 1 <= n && a[x][y + 1] == 0) {
y++;
a[x][y] = now;
now++;
}
while (x + 1 <= n && a[x + 1][y] == 0) {
x++;
a[x][y] = now;
now++;
}
while (y - 1 >= 1 && a[x][y - 1] == 0) {
y--;
a[x][y] = now;
now++;
}
while (x - 1 >= 1 && a[x - 1][y] == 0) {
x--;
a[x][y] = now;
now++;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%3d", a[i][j]);
}
cout << endl;
}
return 0;
}