数组
P5716 [深基3.例9] 月份天数
#include <cstdio>
int main()
{
int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int y, m;
scanf("%d%d", &y, &m);
if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) days[1] = 29;
printf("%d\n", days[m - 1]);
return 0;
}
P1046 [NOIP2005 普及组] 陶陶摘苹果
#include <cstdio>
int main()
{
int h[10];
for (int i = 0; i < 10; i++) scanf("%d", &h[i]);
int t;
scanf("%d", &t);
int ans = 0;
for (int i = 0; i < 10; i++) {
if (t + 30 >= h[i]) ans++;
}
printf("%d\n", ans);
return 0;
}
P1089 [NOIP2004 提高组] 津津的储蓄计划
#include <cstdio>
int main()
{
int b[13];
for (int i = 1; i <= 12; i++) {
scanf("%d", &b[i]);
}
int save = 0;
int money = 0;
for (int i = 1; i <= 12; i++) {
money += 300;
money -= b[i];
if (money < 0) {
printf("%d\n", -i);
break;
}
save += money / 100 * 100;
money = money % 100;
}
if (money >= 0) {
money += save / 5 * 6;
printf("%d\n", money);
}
return 0;
}
P1428 小鱼比可爱
#include <cstdio>
int main()
{
int n;
scanf("%d", &n);
int a[105];
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
int x = 0;
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) ++x;
}
printf("%d ", x);
}
return 0;
}
P1427 小鱼的数字游戏
#include <cstdio>
int main()
{
int x;
int a[105];
int len = 0;
while (scanf("%d", &x)) {
if (x == 0) break;
a[++len] = x;
}
for (int i = len; i >= 1; i--) printf("%d ", a[i]);
return 0;
}
P1047 [NOIP2005 普及组] 校门外的树
#include <cstdio>
int main()
{
int l, m;
int road[10005];
scanf("%d%d", &l, &m);
for (int i = 0; i <= l; i++) road[i] = 1;
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
for (int j = u; j <= v; j++) road[j] = 0;
}
int ans = 0;
for (int i = 0; i <= l; i++) ans += road[i];
printf("%d\n", ans);
return 0;
}
P5728 [深基5.例5] 旗鼓相当的对手
#include <cstdio>
int c[1005], m[1005], e[1005];
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d%d%d", &c[i], &m[i], &e[i]);
int ans = 0;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
int dc = c[i] - c[j];
int dm = m[i] - m[j];
int de = e[i] - e[j];
int dsum = (c[i] + m[i] + e[i]) - (c[j] + m[j] + e[j]);
if (dc >= -5 && dc <= 5)
if (dm >= -5 && dm <= 5)
if (de >= -5 && de <= 5)
if (dsum >= -10 && dsum <= 10)
ans++;
}
printf("%d\n", ans);
return 0;
}
P5729 [深基5.例7] 工艺品制作
#include <cstdio>
int a[25][25][25];
int main()
{
int w, x, h;
scanf("%d%d%d", &w, &x, &h);
int q;
scanf("%d", &q);
int cur = w * x * h;
while (q > 0) {
q--;
int x1, y1, z1, x2, y2, z2;
scanf("%d%d%d%d%d%d", &x1, &y1, &z1, &x2, &y2, &z2);
for (int i = x1; i <= x2; ++i)
for (int j = y1; j <= y2; ++j)
for (int k = z1; k <= z2; ++k) {
if (a[i][j][k] == 0) --cur;
a[i][j][k] = 1;
}
}
printf("%d\n", cur);
return 0;
}
P2550 [AHOI2001] 彩票摇奖
#include <cstdio>
int main()
{
int n;
scanf("%d", &n);
int num[35] = {0};
int award[10] = {0};
for (int i = 0; i < 7; i++) {
int id;
scanf("%d", &id);
num[id] = 1;
}
for (int i = 0; i < n; i++) {
int hit = 0;
for (int j = 0; j < 7; j++) {
int id;
scanf("%d", &id);
if (num[id] == 1) hit++;
}
award[7 - hit]++;
}
for (int i = 0; i <= 6; i++) printf("%d ", award[i]);
return 0;
}
P1554 梦中的统计
#include <cstdio>
int main()
{
int m, n;
scanf("%d%d", &m, &n);
int cnt[10] = {0};
for (int i = m; i <= n; i++) {
int t = i;
while (t > 0) {
cnt[t % 10]++;
t = t / 10;
}
}
for (int i = 0; i <= 9; i++) printf("%d ", cnt[i]);
return 0;
}
P1614 爱与愁的心痛
#include <cstdio>
const int N = 3e3+5;
int a[N];
int main()
{
int n, m; scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
// a[1]+...+a[m]
// a[2]+...+a[m+1]
// ...
// a[n-m+1]+...+a[n]
int ans = 300000;
for (int i = 1; i <= n-m+1; i++) {
int sum = 0;
for (int j = 1; j <= m; j++) {
sum+=a[i+j-1];
}
if (sum<ans) ans=sum;
}
printf("%d\n", ans);
return 0;
}
#include <cstdio>
const int N = 3e3+5;
int a[N];
int main()
{
int n, m; scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
// a[1]+...+a[m]
// a[2]+...+a[m+1]
// ...
// a[n-m+1]+...+a[n]
int sum = 0;
for (int i = 1; i <= m; i++) sum+=a[i];
int ans = sum;
for (int i = 2; i <= n-m+1; i++) {
sum = sum-a[i-1]+a[i+m-1];
if (sum<ans) ans=sum;
}
printf("%d\n", ans);
return 0;
}
P2911 [USACO08OCT] Bovine Bones G
#include <cstdio>
int main()
{
int cnt[100] = {0};
int s1, s2, s3;
scanf("%d%d%d", &s1, &s2, &s3);
for (int i = 1; i <= s1; ++i)
for (int j = 1; j <= s2; ++j)
for (int k = 1; k <= s3; ++k)
cnt[i + j + k]++;
int ans = 3, mx = 0;
for (int i = 3; i <= s1 + s2 + s3; ++i) {
if (cnt[i] > mx) {
mx = cnt[i];
ans = i;
}
}
printf("%d\n", ans);
return 0;
}
P5731 [深基5.习6] 蛇形方阵
#include <cstdio>
const int N = 10;
int a[N][N];
int main()
{
int n; scanf("%d", &n);
int number = 0;
// 一圈一圈填数,n/2圈
for (int i = 1; i <= n / 2; i++) {
// 左上角(i,i) 右下角(n-i+1,n-i+1)
// 向右 (i,i)->(i,n-i)
for (int j = i; j <= n - i; j++)
a[i][j] = ++number;
// 向下 (i,n-i+1)->(n-i,n-i+1)
for (int j = i; j <= n - i; j++)
a[j][n-i+1] = ++number;
// 向左 (n-i+1,n-i+1)->(n-i+1,i+1)
for (int j = n-i+1; j>=i+1; j--)
a[n-i+1][j] = ++number;
// 向上 (n-i+1,i)->(i+1,i)
for (int j = n-i+1; j>=i+1; j--)
a[j][i] = ++number;
}
// 如果n是奇数,还有一个中心点需要填数
if (n%2==1) a[n/2+1][n/2+1]=++number;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) printf("%3d", a[i][j]);
printf("\n");
}
return 0;
}
#include <cstdio>
int main()
{
int n;
scanf("%d", &n);
int s[10][10] = {0};
int x = 1;
int y = 1;
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int d = 0; // 右0下1左2上3,当前方向为右
s[1][1] = 1;
int num = 2;
while (num <= n * n) {
int xx = x + dx[d];
int yy = y + dy[d];
if (xx >= 1 && xx <= n && yy >= 1 && yy <= n && s[xx][yy] == 0) {
x = xx;
y = yy;
s[x][y] = num;
num++;
} else {
d = (d + 1) % 4;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) printf("%3d", s[i][j]);
printf("\n");
}
return 0;
}
P5732 [深基5.习7] 杨辉三角
#include <cstdio>
int main()
{
int n;
int a[25][25] = {0};
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
a[i][1] = 1;
a[i][i] = 1;
for (int j = 2; j <= i - 1; j++)
a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) printf("%d ", a[i][j]);
printf("\n");
}
return 0;
}
P1789 [Mc生存] 插火把
#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
int mp[105][105] = {0};
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
mp[x][y] = 1;
for (int j = max(x - 2, 1); j <= min(x + 2, n); ++j) mp[j][y] = 1;
for (int j = max(y - 2, 1); j <= min(y + 2, n); ++j) mp[x][j] = 1;
for (int a = max(x - 1, 1); a <= min(x + 1, n); ++a)
for (int b = max(y - 1, 0); b <= min(y + 1, n); ++b)
mp[a][b] = 1;
}
for (int i = 0; i < k; ++i) {
int x, y;
scanf("%d%d", &x, &y);
for (int a = max(x - 2, 1); a <= min(x + 2, n); ++a)
for (int b = max(y - 2, 1); b <= min(y + 2, n); ++b)
mp[a][b] = 1;
}
int ans = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (mp[i][j] == 0) ++ans;
printf("%d\n", ans);
return 0;
}
P1319 压缩技术
#include <cstdio>
int main()
{
int n;
scanf("%d", &n);
int num = 0;
int x;
int cnt = 0, sum = 0;
while (sum != n * n) {
scanf("%d", &x);
for (int j = 0; j < x; j++) {
printf("%d", num);
cnt++;
if (cnt == n) {
printf("\n");
cnt = 0;
}
}
num = 1 - num;
sum += x;
}
return 0;
}
P2615 [NOIP2015 提高组] 神奇的幻方
#include <cstdio>
int ans[50][50];
int main()
{
int n;
scanf("%d", &n);
int x = 1, y = (n + 1) / 2;
ans[x][y] = 1;
for (int i = 2; i <= n * n; ++i) {
if (x == 1 && y < n) {
x = n;
++y;
} else if (y == n && x > 1) {
y = 1;
--x;
} else if (x == 1 && y == n) {
++x;
} else if (x != 1 && y != n) {
if (ans[x - 1][y + 1] == 0) {
--x;
++y;
} else ++x;
}
ans[x][y] = i;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) printf("%d ", ans[i][j]);
printf("\n");
}
return 0;
}
P2141 [NOIP2014 普及组] 珠心算测验
注意题目问的是 “其中有多少个数,恰好等于集合中另外两个(不同的)数之和?”,我们可以枚举每个数,去验证能否通过另外的两个数把它加出来。
#include <cstdio>
const int N = 105;
int a[N];
int main()
{
int n; scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
// 验证a[i]是否能被另外两个数加出来
bool ok = false;
for (int j = 1; j <= n; j++) {
for (int k = j + 1; k <= n; k++) {
if (a[i] == a[j] + a[k]) {
ok = true; break;
}
}
if (ok) break;
}
if (ok) ans++;
}
printf("%d\n", ans);
return 0;
}
更进一步地,由于给出的正整数取值范围在 \(10000\) 以内,因此也可以先处理出任意取两个数能加出哪些值来,从而快速验证某个数是否能被另外两个数加出来。
#include <cstdio>
const int N = 105;
const int A = 10005;
bool vis[A];
int a[N];
int main()
{
int n; scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
int sum = a[i] + a[j];
if (sum < A) vis[sum] = true; // 超出值域的情况不需要考虑
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (vis[a[i]]) ans++;
}
printf("%d\n", ans);
return 0;
}
P1904 天际线
记录下轴上每个点的楼房高度的最大值,每个转折点的出现是由于最大高度的变化,所以最后将坐标从左往右枚举一遍,如果该点的高度与前一个点有差距,就说明是转折点,需要输出
考虑如下情况:
如果有两个相邻的楼房三元组,高度信息相同,前者的右端点坐标和后者的左端点坐标刚好差 1,此时这两个点高度没有发生变化,但这两个点都是转折点
针对这个问题,我们可以将原坐标体系放大到 2 倍,让原来的 x 和 x+1 在新坐标体系下分别为 2x 和 2x+2,这样 2x 和 2x+1 之间以及 2x+1 与 2x+2 之间都会存在高度差,从而解决了上面提到的问题
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 20005;
int H[MAXN];
int main()
{
int l, h, r;
while (scanf("%d%d%d", &l, &h, &r) != -1) {
for (int i = l * 2; i <= r * 2 ; i++) H[i] = max(H[i], h);
}
for (int i = 2; i < MAXN; i++)
if (H[i] != H[i - 1]) printf("%d %d ", i / 2, H[i]);
return 0;
}
STL vector
动态数组是大小可以在运行过程中改变的数组。在 C++ 中最流行的动态数据是 vector
,可以像普通数组一样使用它。
下面的代码创建了一个空的 vector
然后向里面添加了三个元素:
vector<int> v;
v.push_back(3); // [3]
v.push_back(2); // [3,2]
v.push_back(5); // [3,2,5]
此时,可以像普通数组一样访问里面的元素:
printf("%d %d %d\n", v[0], v[1], v[2]); // 3 2 5
函数 size
返回 vector
的元素数量。下面的代码遍历整个 vector
并输出每个元素:
for (int i = 0; i < v.size(); i++) {
printf("%d\n", v[i]);
}
一种更简洁的遍历方式如下:
for (int x : v) {
printf("%d\n", x);
}
这种 for 循环被称为 range-based for,每次循环它会按顺序把容器中的元素赋值给循环变量。更进一步,如果不想手动写出容器中元素的类型,可以使用 auto
关键字自动推导元素类型:
for (auto x : v) { // 这里的v因为是vector<int>,所以auto会推导出int
printf("%d\n", x);
}
注意 size
的返回值类型是 size_t
,是一种无符号整型,无符号整型不能表示负数。因此对于下面的代码:
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
int main()
{
vector<int> v;
cout << v.size() << "\n"; // 0
cout << v.size() - 1 << "\n"; // 18446744073709551615
cout << int(v.size()) - 1 << "\n"; // -1
return 0;
}
至于 -1
为什么会变成 18446744073709551615
,这和整数在计算机中的存储方式有关,会在后面讲到。这告诉我们如果想对 size
的返回结果涉及减法运算,可以先将其做类型转换,之后再做减法。这一点在倒序遍历 vector
时很重要:
for (int i = int(v.size()) - 1; i >= 0; i--) {
printf("%d\n", v[i]);
}
上面的代码如果没有将 v.size()
转换成 int
类型,就会在 vector
为空时导致死循环。
函数 back
返回 vector
中的最后一个元素,函数 pop_back
删除最后一个元素:
vector<int> v; v.push_back(5); v.push_back(2);
printf("%d\n", v.back()); // 2
v.pop_back();
printf("%d\n", v.back()); // 5
下面的代码在创建 vector
时用五个元素初始化:
vector<int> v = {2, 4, 2, 5, 1};
另一种创建 vector
的同时给元素赋初始值的方式:
vector<int> v1(10); // 大小为10,每个元素初始值为0
vector<int> v2(10, 5); // 大小为10,每个元素初始值为5
函数 resize(n)
用于将调整 vector
的大小调整至 n
。如果 n
小于当前的大小,会保留前 n
个元素,销毁多出来的部分。如果 n
大于当前的大小,会在末尾补足若干个元素使得新的大小变成 n
,如果调用的是 resize(n,val)
,会用 val
来补足,如果不指定 val
,就用默认的初始化元素。
vector<int> v;
for (int i = 1; i < 10; i++) v.push_back(i);
v.resize(5); // [1,2,3,4,5]
v.resize(8, 100); // [1,2,3,4,5,100,100,100]
v.resize(12); // [1,2,3,4,5,100,100,100,0,0,0,0]
习题:P5727 【深基5.例3】冰雹猜想
解题思路
注意 \(n \le 100\) 只是一开始的 \(n\),变化过程中可能会涉及不止 \(100\) 个中间数,所以不能只开大小 \(100\) 的数组。这里可以使用 vector
来记录中间的数,输出时倒过来输出即可。
#include <cstdio>
#include <vector>
using std::vector;
int main()
{
int n; scanf("%d", &n);
vector<int> ans; // 定义一个里面元素是int类型的动态数组
// 一般不用vector<char> vector<bool>
// 此时是没有ans[0]...的
while (n!=1) {
// 把n添加进ans
ans.push_back(n);
if (n%2==0) {
n/=2;
} else {
n = 3 * n + 1;
}
}
// 把n添加进ans
ans.push_back(n);
// ans.size()
// 尽量不要对size()直接运算,比如 xxx.size()-1
// for (int i=0;i<ans.size();i++) { printf("%d ",ans[i]); }
// for (int i=ans.size()-1;i>=0;i--) { printf("%d ",ans[i]); }
// 尽量不要像上面这样写ans.size()-1
int len = ans.size();
for (int i=len-1; i>=0; i--) {
printf("%d ", ans[i]);
}
return 0;
}
例题:P3613 【深基15.例2】寄包柜
超市里有 \(n \ (n \le 10^5)\) 个寄包柜。每个寄包柜格子数量不一,第 \(i\) 个寄包柜有 \(a_i \ (a_i \le 10^5)\) 个格子,不过并不知道各个 \(a_i\) 的值。对于每个寄包柜,格子编号从 \(1\) 开始,一直到 \(a_i\)。现在有如下 \(q\) 次操作:
1)1 i j k
:在第 \(i\) 个柜子的第 \(j\) 个格子存入物品 \(k \ (0 \le k \le 10^9)\)。当 \(k=0\) 时,说明清空该格子。
2)2 i j
:查询第 \(i\) 个柜子的第 \(j\) 个格子中的物品是什么,保证查询的柜子存过东西。
已知超市里共计不会超过 \(10^7\) 个寄包格子,\(a_i\) 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然,也有可能某些寄包柜中一个格子都没有。
分析:可以建立一个二维数组,s[i][j]
记录第 \(i\) 个柜子中第 \(j\) 个格子中的物品。根据本题的数据规模,需要定义一个大小为 \(10^5 \times 10^5\) 的 int
数组(\(4 \times 10^{10}\) 字节,大约 40GB),显然会超出内存限制。
这里可以用 vector
来解决这个问题。寄包柜最多 \(10^5\) 个,所以可以开一个 \(10^5\) 的数组,但是由于每个寄包柜的格子数不一定,因此数组中的每个元素(每个寄包柜)用一个 vector
来存储,这样就解决了空间的问题。
#include <cstdio>
#include <vector>
using std::vector;
const int N = 1e5 + 5;
vector<int> v[N];
int main()
{
int n, q; scanf("%d%d", &n, &q);
for (int i=1;i<=q;i++) {
int op; scanf("%d", &op);
if (op==1) {
int x,y,z; scanf("%d%d%d", &x,&y,&z);
// resize()调整动态数组大小
if (v[x].size()<=y) v[x].resize(y+1);
v[x][y]=z;
} else {
int x,y; scanf("%d%d",&x,&y);
printf("%d\n", v[x][y]);
}
}
return 0;
}
如果要定义由 10 个动态数组组成的一个二维数组,可以写为 vector<int> v[10]
。甚至动态数组还可以嵌套,定义一个两个维度都不定长的二维数组 vector<vector<int>> v
。