76.最大效益
题目描述
明明的爸爸首先做了一张5行5列的效益表,如下所示:
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
在这张效益表中,每行代表一名公司职员,每列代表一个客户,每行中的5个数字就表示了当该行所代表的公司职员和每位客户谈判时所能产生的效益。明明的爸爸就要通过这张效益表来决定哪位职员与哪位顾客谈判,然后能够使公司的效益最大。就拿上面这张表来看,由于无论哪位职员与哪位客户谈判,所产生的效益都是1,因此最大的效益就是5。这是最简单的一种情况,但是当效益表里的数字变得复杂,就很难进行选择,到底哪种组合方式才是最优的。因此明明的爸爸求助于你,帮助他解决这个问题。
明明的爸爸的问题可以归结为:给你一张5行5列的效益表,表中的数字均为大于等于0的整数,要求在这张表中选出5个数字,使这5个数字的和最大。(注:这5个数字分别来自表中的不同行不同列,即同一行只能选择一个数字,同一列也只能选择一个数字。)
代码
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> benefit(5, vector<int>(5));
vector<bool> visited(5, false); // 记录哪一列(客户)已经被选过了
int max_total_benefit = 0;
// row: 当前正在安排第几位职员 (0~4)
// current_sum: 当前累积的效益总和
void dfs(int row, int current_sum) {
// 递归出口:如果 5 个职员都安排完了
if (row == 5) {
// 更新最大值:保留之前的最大值和当前总和中较大的那个
max_total_benefit = max(max_total_benefit, current_sum);
return;
}
for (int col = 0; col < 5; col++) {
// 如果第 col 位客户还没有被之前的职员选走
if (!visited[col]) {
visited[col] = true; // 标记:这位客户被选了
//去安排下一位职员 (row + 1),把当前效益加进去
dfs(row + 1, current_sum + benefit[row][col]);
visited[col] = false; /
}
}
}
int main() {
while (cin >> benefit[0][0]) {
// 读入第一行的剩余4个数字
for (int j = 1; j < 5; j++) cin >> benefit[0][j];
// 读入剩下的 4 行
for (int i = 1; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cin >> benefit[i][j];
}
}
// 初始化状态:每一组数据都要重置
fill(visited.begin(), visited.end(), false);
max_total_benefit = 0;
// 从第 0 个职员,当前总和为 0 开始搜
dfs(0, 0);
// 输出结果
cout << max_total_benefit << endl;
}
return 0;
}
总结
因为题目要求"同一列只能选择一个数字",所以需要一个数组来记住哪些客户(列)已经被占用了。
回溯 (Backtracking):
- visited[col] = true;(既然我选了这一列,别人就不能选了)
- dfs(...)(继续往下走)
- visited[col] = false; (这一轮尝试结束了,我要把这一列释放出来,给别的组合尝试)
77.螺旋方阵
题目描述
明明在上学的时候,参加数学兴趣班。在班上,老师介绍了一种非常有趣的方阵,称之为螺旋方阵。该方阵一共由n×n个正整数构成(我们称之为n阶螺旋方阵),即共有n行n列。
方阵中的数字从1开始递增,数字的排序规则是从左上角出发由1开始排序,并按顺时针方向旋进,即先排最外面的一圈,然后排里面的一圈,以此类推,直到排到最后一个数为止。
例如一个4阶的螺旋方阵,一共有4×4=16个正整数构成,数字从1递增到16,最后排出来的方阵如下:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
明明回家后想自己动手构造这样的螺旋方阵。他从n=1开始构造,但是他发现当n越来越大时,螺旋方阵的复杂性就越高,然后构造出来的方阵就越容易出错。为了降低构造方阵的出错率,提高构造速度,明明就求助于你,请你帮他写一个程序,来构造螺旋方阵。 明明的问题可以归结为:给你一个正整数n,请你按题目描述中所述的方法,构造出n阶的螺旋方阵。
代码
cpp
#include <iostream>
#include <vector>
using namespace std;
void solve(int n) {
// 创建一个 n x n 的二维数组,初始值为0
vector<vector<int>> matrix(n, vector<int>(n));
int num = 1;
int target = n * n;
int top = 0, bottom = n - 1;
int left = 0, right = n - 1;
while (num <= target) {
// 1. 从左到右 (top 行)
for (int i = left; i <= right && num <= target; i++) {
matrix[top][i] = num++;
}
top++;
// 2. 从上到下 (right 列)
for (int i = top; i <= bottom && num <= target; i++) {
matrix[i][right] = num++;
}
right--;
// 3. 从右到左 (bottom 行)
for (int i = right; i >= left && num <= target; i--) {
matrix[bottom][i] = num++;
}
bottom--;
// 4. 从下到上 (left 列)
for (int i = bottom; i >= top && num <= target; i--) {
matrix[i][left] = num++;
}
left++;
}
// 输出结果
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << matrix[i][j];
// 行末不能有多余空格
if (j < n - 1) cout << " ";
}
cout << endl;
}
}
int main() {
int n;
bool first_case = true;
while (cin >> n) {
if (!first_case) {
cout << endl;
}
solve(n);
first_case = false;
}
return 0;
}
总结
这道题的核心在于模拟。需要模拟数字顺时针旋转填充的过程。
可以定义四个边界变量:
top(上边界):初始为 0bottom(下边界):初始为 n-1left(左边界):初始为 0right(右边界):初始为 n-1
然后在一个循环中,按照 "右 -> 下 -> 左 -> 上" 的顺序填入数字 111 到 n×nn \times nn×n
但第一次提交时,当需要从右往左 (第3步)和从下往上 (第4步)填数,循环变量 i 应该减小(i--),但我习惯性地写成了 i++。这导致 i 永远大于边界值,程序陷入死循环。
78.阵列
题目描述
明明在上学的时候,参加数学兴趣班。在班上,老师介绍了一种非常有趣的阵列。
该阵列由n个正整数构成,阵列中的数字从1开始递增,数字的排序规则是从1开始由中间逆时针向外转出,2出现在1的下面,然后直至输出n为止。
例如当n=5的时候,阵列如下:
5
1 4
2 3
当n=9时,阵列如下:
7 6 5
8 1 4
9 2 3
当n=10时,阵列如下:
7 6 5
8 1 4
9 2 3
10
明明回家后想自己动手构造这样的阵列。他从n=1开始构造,但是他发现当n越来越大时,阵列的复杂性就越高,然后构造出来的阵列就越容易出错。为了降低构造阵列的出错率,提高构造速度,明明就求助于你,请你帮他写一个程序,来构造这样的阵列。
明明的问题可以归结为:给你一个正整数n,请你按题目描述中所述的方法,构造出阵列。
代码
cpp
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
// 方向:下, 右, 上, 左
const int DR[] = {1, 0, -1, 0};
const int DC[] = {0, 1, 0, -1};
void solve() {
int n;
bool first_case = true;
while (cin >> n) {
if (!first_case) {
cout << endl;
}
first_case = false;
int offset = 100;
vector<vector<int>> grid(200, vector<int>(200, 0));
int r = offset, c = offset;
grid[r][c] = 1;
int curr = 2;
int dir = 0;
int step_len = 1;
int step_count = 0;
int turn_count = 0;
int min_r = offset, max_r = offset;
int min_c = offset, max_c = offset;
// 模拟填数
while (curr <= n) {
for (int i = 0; i < step_len; ++i) {
if (curr > n) break;
r += DR[dir];
c += DC[dir];
grid[r][c] = curr++;
if (r < min_r) min_r = r;
if (r > max_r) max_r = r;
if (c < min_c) min_c = c;
if (c > max_c) max_c = c;
}
dir = (dir + 1) % 4;
turn_count++;
if (turn_count % 2 == 0) step_len++;
}
// 计算列宽
vector<int> col_widths;
for (int j = min_c; j <= max_c; ++j) {
int max_val = 0;
for (int i = min_r; i <= max_r; ++i) {
if (grid[i][j] > max_val) max_val = grid[i][j];
}
col_widths.push_back(max_val >= 10 ? 2 : 1);
}
for (int i = min_r; i <= max_r; ++i) {
// 先找到当前行最后一个数字的位置
int last_num_idx = -1;
for (int j = max_c; j >= min_c; --j) {
if (grid[i][j] != 0) {
last_num_idx = j;
break;
}
}
for (int j = min_c; j <= max_c; ++j) {
// 如果当前列超过了该行最后一个数字的位置,直接停止,避免行尾空格
if (j > last_num_idx) break;
int w = col_widths[j - min_c];
if (grid[i][j] != 0) {
cout << setw(w) << grid[i][j];
} else {
// 只有在 last_num_idx 之前的空洞才需要打印空格占位
for (int k = 0; k < w; ++k) cout << " ";
}
if (j < last_num_idx) {
cout << " ";
}
}
cout << endl;
}
}
}
int main() {
solve();
return 0;
}
总结
移动规律:
- 起点是 1。
- 移动方向依次为:下 (Down) -> 右 (Right) -> 上 (Up) -> 左 (Left)。
- 每次移动的步长规律是:1, 1, 2, 2, 3, 3, 4, 4...(每改变两次方向,步长加 1)。
坐标系与边界:
- 因为是从中心向外扩展,坐标会出现负数。
- 为了方便处理,可以先模拟一遍过程,计算出所有数字放置后的最小行/列和最大行/列。
- 或者更简单的做法:开一个足够大的 vector<vector> 或者带偏移量的固定数组(比如中心定在 100,100,因为 N <= 99,半径不会超过 50)。
格式化输出 (关键点):
- 列宽判断:题目要求"当n为两位数时,两位数所在的列占2位"。这意味着我们需要单独检查每一列的最大值。如果某列中出现了 >= 10 的数字,该列所有数字(包括空格占位)都要按 2 位宽度打印;否则按 1 位。
- 对齐:不足位数的左边补空格(右对齐)。
翻译
物联网系统很可能采用事件驱动的架构。在图 12C-2 中,物联网的发展通过一个三层架构来展示。顶层由驱动的应用组成。物联网的应用空间是巨大的。底层代表各种类型的传感设备:即 RFID 标签、ZigBee 或其他类型的传感器,以及道路测绘 GPS 导航仪。传感设备以 RFID 网络、传感器网络和 GPS 的形式进行局域或广域连接。在这些传感设备处收集的信号或信息通过中间层的云计算平台链接到应用。
信号处理云构建在移动网络、互联网骨干网以及中间层的各种信息网络之上。在物联网中,传感事件的含义并不遵循确定性或句法模型。事实上,面向服务的架构(SOA)模型在这里是可采用的。大量的传感器和过滤器被用于收集原始数据
各种计算和存储云及网格被用于处理数据,并将其转换为信息和知识格式。感知到的信息被用于组建针对智能应用的决策系统。中间层也被视为语义网或网格。一些参与者(服务、组件、虚拟化身)是自引用的。
syntactic ------ 句法的 / 语法的
oriented ------ 导向的 / 面向......的 (如 service-oriented 面向服务的)
grids ------ 网格 (计算网格)