NOI C++ 课程体系 / 语法基础 / 第10讲
CSP-J 入门级 · 语法基础 · 文件重定向与STL模板

C++ 文件重定向与STL 完全指南

让输入输出飞出控制台 —— 文件重定向、常用算法函数、四大STL容器,竞赛编程的必备工具箱。

6 大模块 3 个精讲案例 4 种STL容器 5 大易错点 12 道分层练习
本讲 · 第 24/56 讲文件重定向与 STL 模板
模块 01

情景导航

用故事和生活场景建立直觉,再给出严谨的编程定义。

故事小强的"数据搬运"烦恼

小强是一名 CSP-J 备赛选手。他写了一个排序程序,需要输入 500 个测试数据。每次改完代码,他都要重新手动输入一遍。

"要是有办法让程序自动从文件读数据就好了!"

教练教了他文件重定向!从此把数据写进 input.txt,程序自动读取;结果写入 output.txt,效率提升 10 倍!

💡 竞赛中的文件 IO

模式输入输出场景
普通模式键盘 cin屏幕 cout日常练习
freopen文件→cincout→文件竞赛代码内
命令行cin < 文件cout > 文件评测系统
①生活类比
📧

电子邮件

发件人写邮件(文件),收件人打开(文件)。

→ 文件重定向
🍽️

一叠盘子

最后放的盘子最先被拿走(LIFO)。

→ 栈 stack
📋

快递分拣

包裹按到达顺序排队,先到先处理。

→ 队列 queue
📦

自动扩容箱

东西多了箱子自动变大。

→ 向量 vector
②编程定义

文件重定向:将 cin/cout 重新指向文件,而非键盘和屏幕。

STL:C++ 标准模板库,内置高效容器和算法。

📌 本讲核心

类别内容头文件
文件重定向freopen() / 命令行<cstdio>
常用函数min, max, swap, sort<algorithm>
栈 stackLIFO 后进先出<stack>
队列 queueFIFO 先进先出<queue>
向量 vector动态数组<vector>
链表 list双向链表<list>
③核心口诀
🔑 文件重定向

freopen 三参数,文件名·模式·流要记住;
"r"读来"w"写,stdin读 stdout写;
两行代码加上去,cin/cout自动走文件!

🔑 STL容器

栈像盘子叠,后放先取LIFO;
队列像排队,先到先走FIFO;
vector动态数组,随机访问最快;
list双向链表,插入删除灵活!

模块 02

知识探究

深入文件重定向语法,掌握四大函数,理解四种STL容器。

1freopen 语法解剖
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
①函数名②文件名③模式④标准流
C++freopen完整示例
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int a, b;
    cin >> a >> b;        // 自动从文件读
    cout << a + b << endl; // 自动写入文件
    return 0;
}
模式含义注意
"r"只读文件必须存在
"w"写入存在则清空覆盖
"a"追加在末尾追加
freopen 竞赛推荐
  • 只加2行代码
  • cin/cout写法不变
  • 头文件 <cstdio>
  • 提交时记得注释掉!
命令行方式
  • 不修改代码
  • ./a.out < in.txt > out.txt
  • 评测系统标准用法
  • 同一程序测不同数据
2文件流 ifstream / ofstream
C++文件流方式
#include <fstream>
#include <iostream>
using namespace std;
int main() {
    ifstream fin("input.txt");   // 代替cin
    ofstream fout("output.txt"); // 代替cout
    int a, b;
    fin >> a >> b;
    fout << a + b << endl;
    fin.close(); fout.close();
    return 0;
}
3常用函数:min / max / swap
C++min/max/swap
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
    int a = 10, b = 20;
    cout << min(a, b) << endl;  // 10
    cout << max(a, b) << endl;  // 20
    swap(a, b);  // a=20, b=10
    cout << min({3,1,4,1,5}) << endl; // 1
    cout << max({3,1,4,1,5}) << endl; // 5
    return 0;
}
函数功能示例返回
min(a,b)较小值min(10,20)10
max(a,b)较大值max(10,20)20
swap(a,b)交换swap(a,b)无
min({...})多个中最小min({3,1,4})1
4sort 排序 最常用

基于 IntroSort,O(n log n),比冒泡 O(n²) 快数百倍。

sort(v.begin(), v.end()); // 升序
sort(v.begin(), v.end(), greater<int>()); // 降序
①函数名②迭代器范围③比较函数(可选)
C++sort四种用法
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct Stu { string name; int score; };
int main() {
    // 1. 数组升序
    int arr[] = {5,2,8,1};
    sort(arr, arr + 4);

// 2. vector升序
    vector<int> v = {5,2,8,1};
    sort(v.begin(), v.end());

// 3. 降序
    sort(v.begin(), v.end(), greater<int>());

// 4. 结构体按成绩降序
    vector<Stu> stu = {{"A",85},{"B",92},{"C",78}};
    sort(stu.begin(), stu.end(),
         [](const Stu& a, const Stu& b){
             return a.score > b.score;
         });
    for(auto& s : stu)
        cout << s.name << " " << s.score << endl;
    return 0;
}
sort速记

升序:sort(v.begin(), v.end())
降序:sort(v.begin(), v.end(), greater<int>())
注意:end()是尾后迭代器,不含该位置!

💡 sort vs 冒泡

n=10000:冒泡~1亿次,sort~13万次,快770倍!

n=100000:冒泡~100亿次,sort~170万次,快6000倍!

案例案例1:sort+min/max综合 实战

题目:读入n个整数,输出最大值、最小值和排序结果。

C++sort综合
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    int n; cin >> n;
    vector<int> v(n);
    for(int i = 0; i < n; i++) cin >> v[i];

sort(v.begin(), v.end());

cout << "min=" << v.front() << endl;
    cout << "max=" << v.back() << endl;
    cout << "排序: ";
    for(int x : v) cout << x << " ";
    cout << endl;
    return 0;
}
运行结果
输入: 5
      5 2 8 1 9
输出: min=1
      max=9
      排序: 1 2 5 8 9
5栈 stack —— 后进先出 LIFO
🍽️ 栈结构(后进先出)
30
← 栈顶 top(最后入,最先出)
20
10
← 栈底(最先入,最后出)
C++stack操作
#include <stack>
#include <iostream>
using namespace std;
int main() {
    stack<int> s;
    s.push(10); s.push(20); s.push(30);
    cout << s.top() << endl;  // 30
    s.pop();                  // 弹出30(不返回值!)
    cout << s.top() << endl;  // 20
    cout << s.size() << endl; // 2
    cout << s.empty() << endl;// 0(false)
    return 0;
}
操作说明时间
push(x)压入栈顶O(1)
pop()弹出栈顶(不返回值)O(1)
top()查看栈顶元素O(1)
size()元素个数O(1)
empty()是否空O(1)
🔑 栈口诀

栈像一叠盘,后放先取LIFO;
push放 pop拿 top看;
pop()不返回值!先top()再pop()。

🎬 动画演示:stack操作

1
s.push(10) → 栈: [10]
2
s.push(20) → 栈: [10, 20]
3
s.push(30) → 栈: [10, 20, 30]
4
s.top() → 返回 30,栈不变
5
s.pop() → 弹出30,栈: [10, 20]
✓
后进的30先被弹出 = LIFO!
6队列 queue —— 先进先出 FIFO
🚶 队列结构(先进先出)
队首→
10
20
30
←队尾(新元素入)
C++queue操作
#include <queue>
#include <iostream>
using namespace std;
int main() {
    queue<int> q;
    q.push(10); q.push(20); q.push(30);
    cout << q.front() << endl; // 10(队首)
    cout << q.back() << endl;  // 30(队尾)
    q.pop();                   // 弹出队首10
    cout << q.front() << endl; // 20
    cout << q.size() << endl;  // 2
    return 0;
}
操作说明时间
push(x)队尾入队O(1)
pop()队首出队(不返回值)O(1)
front()查看队首O(1)
back()查看队尾O(1)
size()/empty()大小/判空O(1)
stack 🍽️
  • LIFO 后进先出
  • 只操作top一端
  • 场景:括号匹配、DFS
queue 🚶
  • FIFO 先进先出
  • 队尾入、队首出
  • 场景:BFS、排队模拟
案例案例2:括号匹配 栈经典应用

题目:判断 ()[]{} 字符串是否匹配。

C++括号匹配
#include <stack>
#include <string>
#include <iostream>
using namespace std;
bool isValid(string s) {
    stack<char> st;
    for(char c : s) {
        if(c=='(' || c=='[' || c=='{') {
            st.push(c);       // 左括号入栈
        } else {
            if(st.empty()) return false;
            char top = st.top();
            st.pop();
            if(c==')' && top!='(') return false;
            if(c==']' && top!='[') return false;
            if(c=='}' && top!='{') return false;
        }
    }
    return st.empty(); // 栈空=全匹配
}
int main() {
    string s; cin >> s;
    cout << (isValid(s) ? "YES" : "NO") << endl;
    return 0;
}
运行结果
()[]{}   → YES
([)]     → NO
{[()]}   → YES
7向量 vector —— 动态数组 最常用
C++vector操作
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
    vector<int> v;
    v.push_back(10);  // {10}
    v.push_back(20);  // {10,20}
    v.push_back(30);  // {10,20,30}
    v.pop_back();     // {10,20}
    v[0] = 100;      // 下标访问
    cout << v.size() << endl; // 2
    sort(v.begin(), v.end());
    for(int x : v) cout << x << " ";
    v.clear();
    return 0;
}
操作说明时间
push_back(x)末尾插入O(1)均摊
pop_back()删除末尾O(1)
v[i] / at(i)随机访问O(1)
insert/erase中间增删O(n)
size()/clear()大小/清空O(1)/O(n)
8链表 list —— 双向链表
vector 首选95%
  • 内存连续,v[i] 随机访问
  • 末尾操作 O(1),中间 O(n)
  • 配合sort使用频率最高
list
  • 内存不连续,不支持[]
  • 任意位置插入删除 O(1)
  • 自带 sort() 和 unique()
C++list操作
#include <list>
#include <iostream>
using namespace std;
int main() {
    list<int> lst = {3,1,4,1,5};
    lst.push_front(0);  // 头部插入
    lst.sort();           // 自带排序
    lst.unique();         // 去重
    for(int x : lst) cout << x << " ";
    // 0 1 3 4 5
    return 0;
}
案例案例3:vector+文件重定向统计 综合
C++文件统计综合
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    int n; cin >> n;
    vector<int> v(n);
    long long sum = 0;
    for(int i = 0; i < n; i++) {
        cin >> v[i];
        sum += v[i];
    }
    sort(v.begin(), v.end());
    cout << "max=" << v.back() << endl;
    cout << "min=" << v.front() << endl;
    cout << "avg=" << (double)sum / n << endl;
    return 0;
}
data.out
max=100
min=5
avg=52.5
模块 03

实践应用

完整题目练习文件重定向、sort、STL综合运用。

题1成绩排名(sort+vector+freopen)

输入:4个学生姓名和成绩 → 输出:按成绩降序排名

C++成绩排名
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct Stu { string name; int score; };
int main() {
    freopen("score.in", "r", stdin);
    freopen("score.out", "w", stdout);
    int n; cin >> n;
    vector<Stu> stu(n);
    for(int i = 0; i < n; i++)
        cin >> stu[i].name >> stu[i].score;
    sort(stu.begin(), stu.end(),
         [](const Stu& a, const Stu& b) {
             return a.score > b.score;
         });
    for(int i = 0; i < n; i++)
        cout << i+1 << ". " << stu[i].name
             << " " << stu[i].score << endl;
    return 0;
}
score.out
1. Dave 95
2. Bob 92
3. Alice 85
4. Carol 78
题2银行排队系统(queue模拟)

1=入队 2=叫号出队 3=查等待人数

C++银行排队
#include <queue>
#include <iostream>
using namespace std;
int main() {
    queue<int> q;
    int m; cin >> m;
    while(m--) {
        int op; cin >> op;
        if(op == 1) {
            int id; cin >> id;
            q.push(id);
            cout << "入队:" << id << endl;
        } else if(op == 2) {
            if(!q.empty()) {
                cout << "叫号:" << q.front() << endl;
                q.pop();
            } else cout << "空" << endl;
        } else {
            cout << "等待:" << q.size() << endl;
        }
    }
    return 0;
}
运行结果
输入: 6
      1 1001  1 1002  3  2  2  3
输出: 入队:1001
      入队:1002
      等待:2
      叫号:1001
      叫号:1002
      等待:0
题3十进制转二进制(stack应用)
C++进制转换
#include <stack>
#include <iostream>
using namespace std;
int main() {
    int n; cin >> n;
    if(n == 0) { cout << 0 << endl; return 0; }
    stack<int> st;
    while(n > 0) {
        st.push(n % 2);  // 余数入栈
        n /= 2;
    }
    while(!st.empty()) {
        cout << st.top(); // 弹出=逆序输出
        st.pop();
    }
    cout << endl;
    return 0;
}
运行结果
输入: 13  → 输出: 1101
输入: 42  → 输出: 101010

💡 为什么用栈?

先得到的余数是低位,后得到的是高位。输出要高位在前 = "先入后出" = 栈!

模块 04

总结提升

1核心要点总结
知识点核心头文件
freopenfreopen("f","r",stdin) 让cin走文件<cstdio>
min/max返回较小/较大值,支持多参数<algorithm>
swap交换两变量<algorithm>
sortO(n log n)排序,默认升序<algorithm>
stackLIFO:push/pop/top<stack>
queueFIFO:push/pop/front/back<queue>
vector动态数组,随机访问O(1)<vector>
list双向链表,中间插入O(1)<list>
2竞赛万能模板
C++竞赛模板
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    // 文件重定向(提交时注释掉)
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);

int n; cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i++) cin >> a[i];

sort(a.begin(), a.end());

for(int x : a) cout << x << " ";
    cout << endl;
    return 0;
}
3思考与启发

💡 学完本讲的5个道理

1. 文件重定向两行代码搞定竞赛IO。

2. sort比手写排序快数百倍,永远用sort。

3. 栈=LIFO,队列=FIFO,核心区别就进出顺序。

4. vector是最常用容器,能替代普通数组还能动态扩容。

5. 选容器看场景:随机访问→vector,频繁中间插入→list,后进先出→stack,先进先出→queue。

模块 05

学习检测

5个典型错误,每个都有正确写法和预防口诀。

错误1

freopen模式写反

❌ 错误
freopen("in.txt", "w", stdin);
// stdin应该用"r"不是"w"!
✅ 正确
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
口诀:stdin读用"r",stdout写用"w",别搞反!
错误2

stack用front()访问

❌ 错误
stack<int> s;
s.push(10);
cout << s.front();
// 编译错误!栈没有front
✅ 正确
stack<int> s;
s.push(10);
cout << s.top();
// 栈只能用top()
口诀:栈用top,队列用front/back,别串门!
错误3

pop()以为有返回值

❌ 错误
stack<int> s;
s.push(10);
int x = s.pop();
// 编译错误!pop不返回
✅ 正确
stack<int> s;
s.push(10);
int x = s.top(); // 先取值
s.pop();          // 再弹出
口诀:pop只弹不返回,先top再pop!
错误4

sort结束位置写错

❌ 错误
sort(v.begin(), v.end() - 1);
// 少排了最后一个元素!
✅ 正确
sort(v.begin(), v.end());
// end()是尾后,不含该位置
口诀:sort范围[begin,end),end本身不参与!
错误5

vector越界访问

❌ 错误
vector<int> v(5);
cout << v[10];
// 越界!未定义行为
✅ 正确
vector<int> v(5);
cout << v.at(10);
// at()越界会抛异常
口诀:v[i]不检查,v.at(i)会报警!
速查STL速查表
容器/函数头文件关键操作特点
freopen<cstdio>freopen("f","r",stdin)两行搞定文件IO
min/max<algorithm>min(a,b) / max(a,b)支持多参数
swap<algorithm>swap(a,b)无需临时变量
sort<algorithm>sort(begin,end)O(n log n)
stack<stack>push/pop/topLIFO后进先出
queue<queue>push/pop/front/backFIFO先进先出
vector<vector>push_back/[]/size动态数组
list<list>sort()/unique()双向链表
📌知识点笔记卡

📂 文件重定向

freopen("in","r",stdin)

freopen("out","w",stdout)

之后cin/cout自动走文件

⚙️ 四大函数

min/max → 取最值

swap → 交换变量

sort → O(n log n)排序

🍽️ 栈stack

LIFO后进先出

push/pop/top/size/empty

场景:括号匹配、进制转换

🚶 队列queue

FIFO先进先出

push/pop/front/back

场景:BFS、排队模拟

📦 vector

动态数组,最常用

v[i]随机访问O(1)

配合sort频率最高

🔗 list

双向链表

插入删除O(1)

不支持[]访问

课后练习

分层练习作业

A基础 / B提高 / C挑战,难度递增,点击展开答案。

🟢 A组 · 基础(4题)

A1文件重定向

从input.txt读两数,和写入output.txt

input.txt:3 7 → output.txt:10

查看参考答案
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
    return 0;
}

思路:freopen 把 stdin/stdout 重定向到文件,之后 cin/cout 就像平常一样用即可。

易错:input.txt 没放在程序运行目录会读不到;调试时忘了 freopen,屏幕上就看不到输入了。

A2min/max/swap

读入3个数,输出最小值、最大值,交换前两个后输出

输入:5 2 8 → 输出:min=2 max=8 和 2 5

查看参考答案
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
    int a, b, c;
    cin >> a >> b >> c;
    cout << "min=" << min({a, b, c}) << endl;
    cout << "max=" << max({a, b, c}) << endl;
    swap(a, b);
    cout << a << " " << b << endl;
    return 0;
}

思路:min({a,b,c})、max({a,b,c}) 用初始化列表一次比三个数,swap(a,b) 交换前两个。

易错:写成 min(a, b, c) 编译不过——要么加花括号用初始化列表,要么嵌套 min(min(a,b),c)。

A3vector

读入n个数存入vector,逆序输出

输入:5 1 2 3 4 5 → 输出:5 4 3 2 1

查看参考答案
#include <vector>
#include <iostream>
using namespace std;
int main() {
    int n; cin >> n;
    vector<int> v(n);
    for(int i = 0; i < n; i++) cin >> v[i];
    for(int i = n - 1; i >= 0; i--)
        cout << v[i] << " ";
    cout << endl;
    return 0;
}

思路:vector v(n) 开 n 个空间,读入后从下标 n-1 倒序输出。

易错:vector 下标从 0 到 n-1,访问 v[n] 越界不会报错但结果是错的。

A4stack

读入一系列整数,奇数入栈,最后依次弹出输出

输入:5 1 2 3 4 5 → 输出:5 3 1

查看参考答案
#include <stack>
#include <iostream>
using namespace std;
int main() {
    int n; cin >> n;
    stack<int> st;
    for(int i = 0; i < n; i++) {
        int x; cin >> x;
        if(x % 2 != 0) st.push(x);
    }
    while(!st.empty()) {
        cout << st.top() << " ";
        st.pop();
    }
    cout << endl;
    return 0;
}

思路:奇数 push 入栈,最后 while (!st.empty()) 依次 top() 再 pop(),输出顺序正好和入栈相反。

易错:弹出时只写 st.top() 忘了 st.pop(),循环条件永远成立,死循环。

🟡 B组 · 提高(4题)

B1freopen+sort

从input.txt读入n个数,排序后写入output.txt

input.txt:5 3 1 4 1 5 → output.txt:1 1 3 4 5

查看参考答案
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n; cin >> n;
    vector<int> v(n);
    for(int i = 0; i < n; i++) cin >> v[i];
    sort(v.begin(), v.end());
    for(int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

思路:freopen 重定向后读入 vector,sort(v.begin(), v.end()) 排序,结果写入输出文件。

易错:sort 默认升序,要降序得加第三个参数 greater()。

B2queue

模拟进程调度:进程入队(push)、调度(pop)、查等待(size)

输入:5 1 101 1 102 2 3 2

查看参考答案
#include <queue>
#include <iostream>
using namespace std;
int main() {
    queue<int> q;
    int m; cin >> m;
    while(m--) {
        int op; cin >> op;
        if(op == 1) {
            int id; cin >> id;
            q.push(id);
            cout << "入队:" << id << endl;
        } else if(op == 2) {
            if(!q.empty()) {
                cout << "调度:" << q.front() << endl;
                q.pop();
            } else {
                cout << "无进程" << endl;
            }
        } else {
            cout << "等待:" << q.size() << endl;
        }
    }
    return 0;
}

思路:按操作码分发:1 入队、2 出队、3 输出 q.size()。

易错:出队前忘了判 q.empty(),对空队列 pop/front 是未定义行为。

B3list

读入n个数存入list,排序去重后输出

输入:6 3 1 4 1 5 3 → 输出:1 3 4 5

查看参考答案
#include <list>
#include <iostream>
using namespace std;
int main() {
    int n; cin >> n;
    list<int> lst;
    for(int i = 0; i < n; i++) {
        int x; cin >> x;
        lst.push_back(x);
    }
    lst.sort();
    lst.unique();
    for(int x : lst) cout << x << " ";
    cout << endl;
    return 0;
}

思路:push_back 存入 list,lst.sort() 排序后再 lst.unique() 去重输出。

易错:没先 sort 就 unique——unique 只删除相邻的重复元素,乱序时去不干净。

B4stack

用栈实现字符串反转

输入:hello → 输出:olleh

查看参考答案
#include <stack>
#include <string>
#include <iostream>
using namespace std;
int main() {
    string s; cin >> s;
    stack<char> st;
    for(char c : s) st.push(c);
    while(!st.empty()) {
        cout << st.top();
        st.pop();
    }
    cout << endl;
    return 0;
}

思路:字符逐个入栈,弹出顺序天然就是反序,实现字符串反转。

易错:pop() 不返回值,必须先 top() 取字符再 pop(),只写 pop() 什么都拿不到。

🔴 C组 · 挑战(4题)

C1stack高级

用栈判断回文串(如 abcba 是回文)

输入:abcba → 输出:YES

查看参考答案
#include <stack>
#include <string>
#include <iostream>
using namespace std;
int main() {
    string s; cin >> s;
    stack<char> st;
    int n = s.size();
    // 前半入栈
    for(int i = 0; i < n / 2; i++)
        st.push(s[i]);
    // 后半与栈顶比较
    int start = (n % 2 == 0) ? n / 2 : n / 2 + 1;
    bool ok = true;
    for(int i = start; i < n; i++) {
        if(st.empty() || st.top() != s[i]) {
            ok = false; break;
        }
        st.pop();
    }
    cout << (ok ? "YES" : "NO") << endl;
    return 0;
}

解析:前半入栈,后半逐个与栈顶比较。栈的LIFO特性正好实现"反转对比"。

易错:奇数长度时中间字符下标算错——后半段应从 s[n/2 + 1] 开始,跳过正中间那个字符。

C2freopen综合

从文件读一行文本,统计字母、数字、空格、其他字符个数

input.txt:Hello World! 2024

查看参考答案
#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <iostream>
using namespace std;
int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    string line;
    getline(cin, line);
    int alpha = 0, digit = 0;
    int space = 0, other = 0;
    for(char c : line) {
        if(isalpha(c)) alpha++;
        else if(isdigit(c)) digit++;
        else if(isspace(c)) space++;
        else other++;
    }
    cout << "字母:" << alpha << endl;
    cout << "数字:" << digit << endl;
    cout << "空格:" << space << endl;
    cout << "其他:" << other << endl;
    return 0;
}

思路:freopen 重定向后 getline 读整行,逐字符按 isalpha、isdigit、空格分类计数,其余算其他。

易错:Windows 下的文件行尾带 '\r',getline 读进来的行尾会混入它,影响“其他字符”的统计。

C3vector通讯录

用vector实现简单通讯录:添加、查找、删除、显示

查看参考答案
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct Contact { string name, phone; };
int main() {
    vector<Contact> book;
    int m; cin >> m;
    while(m--) {
        int op; cin >> op;
        if(op == 1) { // 添加
            Contact c;
            cin >> c.name >> c.phone;
            book.push_back(c);
            cout << "添加成功" << endl;
        } else if(op == 2) { // 查找
            string name; cin >> name;
            bool found = false;
            for(auto& c : book) {
                if(c.name == name) {
                    cout << c.phone << endl;
                    found = true; break;
                }
            }
            if(!found) cout << "未找到" << endl;
        } else if(op == 3) { // 删除
            string name; cin >> name;
            for(auto it = book.begin();
                 it != book.end(); ++it) {
                if(it->name == name) {
                    book.erase(it);
                    cout << "删除成功" << endl;
                    break;
                }
            }
        } else { // 显示全部
            for(auto& c : book)
                cout << c.name << " "
                     << c.phone << endl;
        }
    }
    return 0;
}

思路:vector 用 push_back 添加,遍历比对姓名查找,erase 删除,最后遍历显示。

易错:在遍历中边删边用下标前进,erase 后元素前移会跳过下一个——删除后要让下标回退或用迭代器。

C4综合竞赛

读入n个学生学号+三门成绩,算总分,按总分降序输出(freopen+sort+vector)

查看参考答案
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct Stu {
    int id;
    int chinese, math, english;
    int total() const {
        return chinese + math + english;
    }
};
int main() {
    freopen("stu.in", "r", stdin);
    freopen("stu.out", "w", stdout);
    int n; cin >> n;
    vector<Stu> stu(n);
    for(int i = 0; i < n; i++) {
        cin >> stu[i].id >> stu[i].chinese
            >> stu[i].math >> stu[i].english;
    }
    sort(stu.begin(), stu.end(),
         [](const Stu& a, const Stu& b) {
             return a.total() > b.total();
         });
    for(int i = 0; i < n; i++) {
        cout << i + 1 << " "
             << stu[i].id << " "
             << stu[i].total() << endl;
    }
    return 0;
}

解析:结构体中定义 total() 方法算总分,sort 用 lambda 按总分降序。freopen 实现文件读写。这是竞赛中最常见的综合模板。

易错:sort 的比较函数里忘了把成员函数写成 const,或者总分相同时没按学号排导致输出顺序不稳定。