大学生程序设计竞赛,我得了个0蛋

235 阅读12分钟

最近有个没毕业的粉丝找我看了一下简历,在学校(湖南工业大学)算是非常优秀了,参加很多比赛,也拿到过很多奖。毕竟我当年上大学的时候,根本知道这些比赛是干嘛的,别说参与了。

image.png 好奇看了一下相关比赛的题目,自己琢磨了半天,算了,前端仔,我不配。下面是具体的题目和答案,大家看看自己能不能做出来。

大学生程序设计竞赛模拟题

比赛时长: 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

题目五:数据压缩优化(困难)

题目描述: 设计一个数据压缩算法,将给定的字符串进行压缩。压缩后的字符串需要满足:

  1. 压缩后的大小不超过原字符串
  2. 可以无损还原
  3. 压缩率尽可能高

输入格式:

  • 第一行一个整数T,表示测试用例数量
  • 接下来T行,每行一个字符串S(长度不超过1000,只包含小写字母)

输出格式:

  • 对于每个测试用例,输出一行压缩后的字符串

样例输入:

3
aaabbb
abcdef
aaaaaa

样例输出:

a3b3
abcdef
a6

评分标准

  1. 每题100分,共500分

  2. 部分分制:

    • 通过样例测试:20%
    • 通过小规模数据:30%
    • 通过中等规模数据:30%
    • 通过大规模数据:20%
  3. 时间限制:

    • C/C++:1秒
    • Java:2秒
    • Python:3秒
  4. 内存限制:

    • 256MB

注意事项

  1. 所有输入输出均使用标准输入输出流
  2. 请注意处理边界情况
  3. 注意代码的时间复杂度和空间复杂度
  4. 确保输出格式与要求完全一致

这套题目涵盖了多个算法知识点:

  • 贪心算法
  • 图论(最短路、最小生成树)
  • 动态规划
  • 字符串处理
  • 网络流

下面给出 C++ 和 JavaScript 的答案。

C++ 版本答案

题目一:智能仓储调度

解题思路:

  1. 计算所有货架商品总数
  2. 计算平均数,判断是否能平均分配
  3. 统计需要移出和移入的商品数量
  4. 计算最少操作次数
#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;
}

题目二:网络延迟优化

解题思路:

  1. 使用Floyd-Warshall算法计算所有点对之间的最短路径
  2. 枚举所有可能的K个服务器节点组合
  3. 对每种组合,计算其他节点到最近服务器的距离
  4. 维护全局最小值
#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;
}

题目三:基因序列分析

解题思路:

  1. 对每个模式串进行处理
  2. 使用KMP算法的变体处理通配符
  3. 记录所有匹配位置
#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;
}

题目四:智能物流规划

解题思路:

  1. 使用最小费用最大流算法
  2. 建立源点和汇点
  3. 连接配送点和目标点,边权为曼哈顿距离
  4. 求解最小费用最大流
#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;
}

题目五:数据压缩优化

解题思路:

  1. 使用游程编码(Run-Length Encoding)
  2. 对连续相同字符进行计数
  3. 如果压缩后长度不小于原长度,则保持原样
  4. 处理特殊情况
#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版本的实现可能更加简洁,比如:

  1. 数组操作更加便捷(reduce, map等方法)
  2. 字符串处理更加简单
  3. 不需要显式的内存管理
  4. 生成器函数可以优雅地处理组合问题

但是,JavaScript在处理大规模数据时会比C++更慢,特别是在需要大量数值计算的场景下。在实际竞赛中,没人会用JavaScript去搞的。这里只是为了前端友好,特意给了一份答案。