最近有个没毕业的粉丝找我看了一下简历,在学校(湖南工业大学)算是非常优秀了,参加很多比赛,也拿到过很多奖。毕竟我当年上大学的时候,根本知道这些比赛是干嘛的,别说参与了。
好奇看了一下相关比赛的题目,自己琢磨了半天,算了,前端仔,我不配。下面是具体的题目和答案,大家看看自己能不能做出来。
大学生程序设计竞赛模拟题
比赛时长: 5小时 编程语言: C/C++, Java, Python 评分标准: 每题100分,共500分
题目一:智能仓储调度(简单)
题目描述: 某智能仓库有N个货架,每个货架上有不同数量的商品。现在需要调度机器人完成取货任务,使得所有货架的商品数量相等。每次操作可以将一个货架上的一件商品移动到另一个货架。请计算最少需要多少次操作。
输入格式:
- 第一行一个整数N(2 ≤ N ≤ 10^5),表示货架数量
- 第二行N个整数,表示每个货架上的商品数量(0 ≤ 数量 ≤ 10^9)
输出格式:
- 输出一个整数,表示最少操作次数
样例输入:
4
1 5 5 5
样例输出:
3
题目二:网络延迟优化(中等)
题目描述: 有一个包含N个节点的网络,节点之间通过M条双向链路连接。每条链路都有一个延迟值。现在需要在网络中选择K个节点作为服务器节点,使得其他所有节点到最近的服务器节点的延迟最小。请计算这个最小延迟值。
输入格式:
- 第一行三个整数N、M、K(1 ≤ K < N ≤ 1000,1 ≤ M ≤ 10^5)
- 接下来M行,每行三个整数u、v、w,表示节点u和v之间有一条延迟为w的链路
输出格式:
- 输出一个整数,表示最小延迟值
样例输入:
5 6 2
1 2 1
2 3 2
3 4 3
4 5 4
1 5 5
2 4 2
样例输出:
2
题目三:基因序列分析(中等)
题目描述: 给定一个DNA序列(只包含A、C、G、T四种字符)和若干个模式串,请找出所有模式串在DNA序列中出现的位置。要求支持模式串中包含通配符'*'(可以匹配任意个字符)。
输入格式:
- 第一行一个字符串S,表示DNA序列
- 第二行一个整数N,表示模式串数量
- 接下来N行,每行一个字符串P,表示一个模式串
输出格式:
- 对于每个模式串,输出一行,包含所有匹配位置(从1开始计数)
样例输入:
ACGTACGT
2
A*T
GTA
样例输出:
1 5
3
题目四:智能物流规划(困难)
题目描述: 在一个N×N的网格地图中,有M个配送点和K个目标点。每个配送点有一定数量的货物,每个目标点需要一定数量的货物。现在需要规划配送路线,使得总配送距离最小。配送车在移动时只能上下左右移动。
输入格式:
- 第一行三个整数N、M、K(1 ≤ N ≤ 100,1 ≤ M,K ≤ 10)
- 接下来M行,每行三个整数x、y、w,表示配送点坐标和货物数量
- 接下来K行,每行三个整数x、y、w,表示目标点坐标和需求数量
输出格式:
- 输出一个整数,表示最小总配送距离
样例输入:
5 2 2
1 1 5
5 5 5
1 5 3
5 1 7
样例输出:
16
题目五:数据压缩优化(困难)
题目描述: 设计一个数据压缩算法,将给定的字符串进行压缩。压缩后的字符串需要满足:
- 压缩后的大小不超过原字符串
- 可以无损还原
- 压缩率尽可能高
输入格式:
- 第一行一个整数T,表示测试用例数量
- 接下来T行,每行一个字符串S(长度不超过1000,只包含小写字母)
输出格式:
- 对于每个测试用例,输出一行压缩后的字符串
样例输入:
3
aaabbb
abcdef
aaaaaa
样例输出:
a3b3
abcdef
a6
评分标准
-
每题100分,共500分
-
部分分制:
- 通过样例测试:20%
- 通过小规模数据:30%
- 通过中等规模数据:30%
- 通过大规模数据:20%
-
时间限制:
- C/C++:1秒
- Java:2秒
- Python:3秒
-
内存限制:
- 256MB
注意事项
- 所有输入输出均使用标准输入输出流
- 请注意处理边界情况
- 注意代码的时间复杂度和空间复杂度
- 确保输出格式与要求完全一致
这套题目涵盖了多个算法知识点:
- 贪心算法
- 图论(最短路、最小生成树)
- 动态规划
- 字符串处理
- 网络流
下面给出 C++ 和 JavaScript 的答案。
C++ 版本答案
题目一:智能仓储调度
解题思路:
- 计算所有货架商品总数
- 计算平均数,判断是否能平均分配
- 统计需要移出和移入的商品数量
- 计算最少操作次数
#include <iostream>
#include <vector>
using namespace std;
long long minOperations(vector<long long>& shelves) {
int n = shelves.size();
// 计算总数
long long sum = 0;
for (long long x : shelves) {
sum += x;
}
// 判断是否能平均分配
if (sum % n != 0) return -1;
long long avg = sum / n;
long long ops = 0;
// 计算需要移动的商品数量
for (long long x : shelves) {
if (x > avg) {
ops += x - avg;
}
}
return ops;
}
int main() {
int n;
cin >> n;
vector<long long> shelves(n);
for (int i = 0; i < n; i++) {
cin >> shelves[i];
}
cout << minOperations(shelves) << endl;
return 0;
}
题目二:网络延迟优化
解题思路:
- 使用Floyd-Warshall算法计算所有点对之间的最短路径
- 枚举所有可能的K个服务器节点组合
- 对每种组合,计算其他节点到最近服务器的距离
- 维护全局最小值
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 1e9;
int minDelay(int n, int m, int k, vector<vector<int>>& edges) {
// 初始化距离矩阵
vector<vector<int>> dist(n, vector<int>(n, INF));
for (int i = 0; i < n; i++) dist[i][i] = 0;
// 建图
for (auto& e : edges) {
int u = e[0] - 1, v = e[1] - 1, w = e[2];
dist[u][v] = dist[v][u] = min(dist[u][v], w);
}
// Floyd-Warshall
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] != INF && dist[k][j] != INF) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
// 枚举所有可能的服务器组合
vector<int> servers;
for (int i = 0; i < k; i++) servers.push_back(1);
for (int i = 0; i < n-k; i++) servers.push_back(0);
int minMaxDelay = INF;
do {
int maxDelay = 0;
for (int i = 0; i < n; i++) {
if (servers[i]) continue;
int minDist = INF;
for (int j = 0; j < n; j++) {
if (servers[j]) {
minDist = min(minDist, dist[i][j]);
}
}
maxDelay = max(maxDelay, minDist);
}
minMaxDelay = min(minMaxDelay, maxDelay);
} while (prev_permutation(servers.begin(), servers.end()));
return minMaxDelay;
}
题目三:基因序列分析
解题思路:
- 对每个模式串进行处理
- 使用KMP算法的变体处理通配符
- 记录所有匹配位置
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 判断字符串是否匹配(带通配符)
bool isMatch(string& s, string& p, int start) {
int i = start, j = 0;
while (i < s.length() && j < p.length()) {
if (p[j] == '*') {
// 通配符可以匹配0个或多个字符
return true;
} else if (s[i] != p[j]) {
return false;
}
i++; j++;
}
return j == p.length();
}
vector<int> findPattern(string& s, string& p) {
vector<int> result;
// 遍历所有可能的起始位置
for (int i = 0; i <= s.length() - p.length(); i++) {
if (isMatch(s, p, i)) {
result.push_back(i + 1); // 1-based indexing
}
}
return result;
}
int main() {
string dna;
cin >> dna;
int n;
cin >> n;
vector<string> patterns(n);
for (int i = 0; i < n; i++) {
cin >> patterns[i];
}
// 处理每个模式串
for (string& p : patterns) {
vector<int> positions = findPattern(dna, p);
for (int pos : positions) {
cout << pos << " ";
}
cout << endl;
}
return 0;
}
题目四:智能物流规划
解题思路:
- 使用最小费用最大流算法
- 建立源点和汇点
- 连接配送点和目标点,边权为曼哈顿距离
- 求解最小费用最大流
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int INF = 1e9;
struct Point {
int x, y, w;
};
// 计算曼哈顿距离
int distance(Point& a, Point& b) {
return abs(a.x - b.x) + abs(a.y - b.y);
}
int minTotalDistance(int n, vector<Point>& sources, vector<Point>& targets) {
int m = sources.size();
int k = targets.size();
// 建图
vector<vector<int>> cost(m + k + 2, vector<int>(m + k + 2, INF));
vector<vector<int>> cap(m + k + 2, vector<int>(m + k + 2, 0));
int source = m + k;
int sink = m + k + 1;
// 连接源点到配送点
for (int i = 0; i < m; i++) {
cost[source][i] = 0;
cap[source][i] = sources[i].w;
}
// 连接目标点到汇点
for (int i = 0; i < k; i++) {
cost[m + i][sink] = 0;
cap[m + i][sink] = targets[i].w;
}
// 连接配送点到目标点
for (int i = 0; i < m; i++) {
for (int j = 0; j < k; j++) {
cost[i][m + j] = distance(sources[i], targets[j]);
cap[i][m + j] = min(sources[i].w, targets[j].w);
}
}
// 最小费用最大流算法
int totalCost = 0;
while (true) {
vector<int> dist(m + k + 2, INF);
vector<int> prev(m + k + 2, -1);
vector<bool> inq(m + k + 2, false);
queue<int> q;
dist[source] = 0;
q.push(source);
inq[source] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for (int v = 0; v < m + k + 2; v++) {
if (cap[u][v] > 0 && dist[v] > dist[u] + cost[u][v]) {
dist[v] = dist[u] + cost[u][v];
prev[v] = u;
if (!inq[v]) {
q.push(v);
inq[v] = true;
}
}
}
}
if (dist[sink] == INF) break;
int flow = INF;
for (int v = sink; v != source; v = prev[v]) {
flow = min(flow, cap[prev[v]][v]);
}
for (int v = sink; v != source; v = prev[v]) {
totalCost += flow * cost[prev[v]][v];
cap[prev[v]][v] -= flow;
cap[v][prev[v]] += flow;
}
}
return totalCost;
}
题目五:数据压缩优化
解题思路:
- 使用游程编码(Run-Length Encoding)
- 对连续相同字符进行计数
- 如果压缩后长度不小于原长度,则保持原样
- 处理特殊情况
#include <iostream>
#include <string>
using namespace std;
string compress(string& s) {
if (s.empty()) return s;
string result;
char curr = s[0];
int count = 1;
// 统计连续字符
for (int i = 1; i < s.length(); i++) {
if (s[i] == curr) {
count++;
} else {
// 添加当前字符和计数
result += curr;
if (count > 1) {
result += to_string(count);
}
curr = s[i];
count = 1;
}
}
// 处理最后一组
result += curr;
if (count > 1) {
result += to_string(count);
}
// 如果压缩后更长,返回原字符串
return result.length() < s.length() ? result : s;
}
int main() {
int t;
cin >> t;
while (t--) {
string s;
cin >> s;
cout << compress(s) << endl;
}
return 0;
}
JavaScript 版本 的答案
题目一:智能仓储调度
/**
* 计算使所有货架商品数量相等的最少操作次数
* @param {number[]} shelves 货架数组
* @return {number} 最少操作次数
*/
function minOperations(shelves) {
// 计算总数和平均值
const sum = shelves.reduce((a, b) => a + b, 0);
const n = shelves.length;
// 判断是否能平均分配
if (sum % n !== 0) return -1;
const avg = Math.floor(sum / n);
let ops = 0;
// 计算需要移动的商品数量
// 只需要计算大于平均值的部分,因为移出等于移入
for (const count of shelves) {
if (count > avg) {
ops += count - avg;
}
}
return ops;
}
// 测试代码
const shelves = [1, 5, 5, 5];
console.log(minOperations(shelves)); // 输出: 3
题目二:网络延迟优化
/**
* 计算网络中最小延迟值
* @param {number} n 节点数量
* @param {number} k 服务器数量
* @param {number[][]} edges 边的信息
* @return {number} 最小延迟值
*/
function minDelay(n, k, edges) {
// 初始化距离矩阵
const dist = Array(n).fill().map(() => Array(n).fill(Infinity));
for (let i = 0; i < n; i++) {
dist[i][i] = 0;
}
// 建图
for (const [u, v, w] of edges) {
dist[u-1][v-1] = Math.min(dist[u-1][v-1], w);
dist[v-1][u-1] = Math.min(dist[v-1][u-1], w);
}
// Floyd-Warshall算法
for (let k = 0; k < n; k++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (dist[i][k] !== Infinity && dist[k][j] !== Infinity) {
dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
// 生成所有可能的服务器组合
function* combinations(arr, k) {
const n = arr.length;
const result = Array(k);
function* generate(start, pos) {
if (pos === k) {
yield result.slice();
return;
}
for (let i = start; i < n; i++) {
result[pos] = arr[i];
yield* generate(i + 1, pos + 1);
}
}
yield* generate(0, 0);
}
// 计算最小延迟
let minMaxDelay = Infinity;
const nodes = Array.from({length: n}, (_, i) => i);
for (const servers of combinations(nodes, k)) {
let maxDelay = 0;
for (let i = 0; i < n; i++) {
if (servers.includes(i)) continue;
let minDist = Infinity;
for (const server of servers) {
minDist = Math.min(minDist, dist[i][server]);
}
maxDelay = Math.max(maxDelay, minDist);
}
minMaxDelay = Math.min(minMaxDelay, maxDelay);
}
return minMaxDelay;
}
题目三:基因序列分析
/**
* 在DNA序列中查找模式串
* @param {string} dna DNA序列
* @param {string} pattern 模式串
* @return {number[]} 匹配位置数组
*/
function findPattern(dna, pattern) {
const result = [];
// 判断字符串是否匹配(带通配符)
function isMatch(s, p, start) {
let i = start;
let j = 0;
while (i < s.length && j < p.length) {
if (p[j] === '*') {
return true; // 通配符可以匹配任意字符
}
if (s[i] !== p[j]) {
return false;
}
i++;
j++;
}
return j === p.length;
}
// 查找所有匹配位置
for (let i = 0; i <= dna.length - pattern.length; i++) {
if (isMatch(dna, pattern, i)) {
result.push(i + 1); // 1-based indexing
}
}
return result;
}
// 主函数
function processPatterns(dna, patterns) {
return patterns.map(pattern => findPattern(dna, pattern));
}
题目四:智能物流规划
/**
* 计算最小配送距离
* @param {number} n 网格大小
* @param {Object[]} sources 配送点信息
* @param {Object[]} targets 目标点信息
* @return {number} 最小总配送距离
*/
function minTotalDistance(n, sources, targets) {
// 计算曼哈顿距离
function distance(a, b) {
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
}
// 使用匈牙利算法求解最小权值匹配
function hungarianAlgorithm(cost) {
const n = cost.length;
const m = cost[0].length;
const lx = Array(n).fill(0);
const ly = Array(m).fill(0);
const match = Array(m).fill(-1);
const slack = Array(m).fill(0);
const visx = Array(n).fill(false);
const visy = Array(m).fill(false);
// 寻找增广路
function find(x) {
visx[x] = true;
for (let y = 0; y < m; y++) {
if (visy[y]) continue;
const t = lx[x] + ly[y] - cost[x][y];
if (t === 0) {
visy[y] = true;
if (match[y] === -1 || find(match[y])) {
match[y] = x;
return true;
}
} else {
slack[y] = Math.min(slack[y], t);
}
}
return false;
}
// 初始化标号
for (let i = 0; i < n; i++) {
lx[i] = Math.max(...cost[i]);
}
// 求解最大匹配
for (let i = 0; i < n; i++) {
while (true) {
visx.fill(false);
visy.fill(false);
slack.fill(Infinity);
if (find(i)) break;
let delta = Infinity;
for (let j = 0; j < m; j++) {
if (!visy[j]) {
delta = Math.min(delta, slack[j]);
}
}
for (let j = 0; j < n; j++) {
if (visx[j]) lx[j] -= delta;
}
for (let j = 0; j < m; j++) {
if (visy[j]) ly[j] += delta;
else slack[j] -= delta;
}
}
}
// 计算最小权值和
let result = 0;
for (let i = 0; i < m; i++) {
if (match[i] !== -1) {
result += cost[match[i]][i];
}
}
return result;
}
// 构建成本矩阵
const cost = Array(sources.length).fill().map(() =>
Array(targets.length).fill(0)
);
for (let i = 0; i < sources.length; i++) {
for (let j = 0; j < targets.length; j++) {
cost[i][j] = distance(sources[i], targets[j]);
}
}
return hungarianAlgorithm(cost);
}
题目五:数据压缩优化
/**
* 压缩字符串
* @param {string} s 输入字符串
* @return {string} 压缩后的字符串
*/
function compress(s) {
if (!s) return s;
let result = '';
let curr = s[0];
let count = 1;
// 统计连续字符
for (let i = 1; i < s.length; i++) {
if (s[i] === curr) {
count++;
} else {
// 添加当前字符和计数
result += curr;
if (count > 1) {
result += count;
}
curr = s[i];
count = 1;
}
}
// 处理最后一组
result += curr;
if (count > 1) {
result += count;
}
// 如果压缩后更长,返回原字符串
return result.length < s.length ? result : s;
}
// 主函数
function processStrings(strings) {
return strings.map(compress);
}
// 测试代码
const testCases = ['aaabbb', 'abcdef', 'aaaaaa'];
console.log(processStrings(testCases));
// 输出: ['a3b3', 'abcdef', 'a6']
JavaScript版本的实现可能更加简洁,比如:
- 数组操作更加便捷(reduce, map等方法)
- 字符串处理更加简单
- 不需要显式的内存管理
- 生成器函数可以优雅地处理组合问题
但是,JavaScript在处理大规模数据时会比C++更慢,特别是在需要大量数值计算的场景下。在实际竞赛中,没人会用JavaScript去搞的。这里只是为了前端友好,特意给了一份答案。