https://www.nowcoder.com/ta/huawei

大部分简单题会使用 ruby 编写,除非输入比较难处理或者 C++ 写起来更方便.

HJ108 最小公倍数

=begin # 公式
    gcd(a, b) = !b ? a : gcd(a % b, a)
    lcm(a, b) = a * b / gcd(a, b)
=end
p gets.split.map(&:to_i).reduce :lcm

HJ107 求解立方根 二分

#include <iostream>
#include <cstdio>
using namespace std;
int main() {
    double i, o;
    cin >> i;
    if (i == 0 || i == -1 || i == 1) o = i;
    else {
        // 二分模板: l 左端点, r 右端点, t 临时变量
        double l = i > 0 ? 0 : i, r = i > 0 ? i : 0, t;
        while ((r - l) >= 0.02) { // 当求解区域不够小时
            o = (l + r) / 2, t = o * o * o; // 取中间点, 和目标比较
            if (t > i) r = o;
            else if (t < i) l = o;
            else break; // 正好
        }
    }
    printf("%.1f\n", o);
}

HJ106 字符逆序

puts gets.chomp.reverse

HJ105 记负均正II

#include <iostream>
#include <cstdio>
using namespace std;
int main() {
    int i, k = 0, n = 0;
    double d;
    while (cin >> i) {
        if (i < 0) k++;
        else d += i, n++;
    }
    cout << k << endl;
    printf("%.1f\n", n ? d / n : 0.0);
}

HJ104 字符串分割

#include <stdio.h>
#include <string.h>
int main() {
    int i, j, n;
    char s[100], a[20]; // s 输入, a 存储每 8 个字符
    while (scanf("%d", &n) == 1)
        for (i = 0; i < n; ++i) {
            scanf("%s", s);
            int len = strlen(s);
            int r = len % 8; // 最后一行有几个有效字符
            int m = len / 8 + (r != 0); // 一共输出 m 行
            for (j = 0; j < m; ++j) {
                sscanf(s + j * 8, "%8s", a); // 扫(最多) 8 个字符
                printf("%s", a);
                if (j == m - 1 && r != 0) { // 如果是最后一行, 补 0
                    printf("%0*d", 8 - r, 0); // 输出 8 - r 个 0
                }
                printf("\n");
            }
        }
}

HJ103 Redraiment的走法 最大上升子序列

设 a(i) = i 格高度, f(i) = 到 i 格最多有几步, 显然 f(0) = 1;
以后每格,是保持上升规律的前提下最大的 f(k) + 1, 即 f(i) = (f(0)..f(i-1)).filter_by_index { |k| a[i] > a[k] }.max + 1.

#include <iostream>
using namespace std;
int a[10000], dp[10000];
int main() {
    int n, m;
    while (cin >> n) {
        m = 0;
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
            dp[i] = 1; // 最少也有 1 步
            for (int j = 0; j < i; ++j) {
                if (a[i] > a[j]) { // 保持上升规律
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            m = max(m, dp[i]);
        }
        cout << m << endl;
    }
    return 0;
}

HJ102 字符统计

本来是一个统计 + 自定义排序 (sort_by { |e| [-e.count, e[0].ord] }), 不过看到字符类问题, 可以用一个 int count[128] 顺便完成字典序, 下面是暴力写法.

#include <iostream>
#include <string>
using namespace std;
int main() {
    string s;
    int count[256];
    while (cin >> s) {
        for (int i = 0; i < 256; ++i) count[i] = 0;
        for (char c : s) count[int(c)]++;
        char a; int max;
        do {
            max = 0;
            for (int i = 0; i < 256; ++i)
                if (count[i] > max) max = count[i], a = char(i);
            if (max > 0) {
                count[int(a)] = 0;
                cout << a;
            }
        } while (max > 0);
        cout << endl;
    }
}

HJ101 简单排序

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
#define all(r) begin(r), end(r)
int main() {
    int n;
    while (cin >> n) {
        vector<int> v;
        for (int i = 0, a; i < n; ++i) {
            cin >> a;
            v.push_back(a);
        }
        bool desc;
        cin >> desc;
        sort(all(v));
        if (desc) reverse(all(v));
        copy(all(v), ostream_iterator<int>(cout, " "));
        cout << endl;
    }
}

HJ100 等差数列求和

#include <iostream>
using namespace std;
int main() {
    int n;
    while (cin >> n) {
        cout << (3 * n + 1) * n / 2 << endl;
    }
    return 0;
}

HJ99 自守数

本来是一个 (i**2).to_s.end_with?(i.to_s), 不过 int 也就 10 位, 平方能进 int 的就更少了, 可以暴力.

#include <iostream>
using namespace std;
int main() {
    int n, k;
    while (cin >> n) {
        k = 0;
        for (int i = 0, j; i <= n; ++i) {
            j = i * i;
            if ((i >= 10000 && j % 100000 == i) ||
                (i >= 1000  && j % 10000  == i) ||
                (i >= 100   && j % 1000   == i) ||
                (i >= 10    && j % 100    == i) ||
                (i >= 0     && j % 10     == i)) k++;
        }
        cout << k << endl;
    }
    return 0;
}

HJ98 自动收货系统

辣鸡题, bug 多, 8做.

HJ97 记负均正

HJ105 的代码可以直接过.

HJ96 数字前后加上 *

while s = gets
    puts s.scan(/\d+|\D+/).map { |e| e =~ /\d/ ? "*#{e}*" : e }.join
end

HJ95 人民币转换

辣鸡题, 8做.

HJ94 记票统计

while gets
    k = {}
    gets.split.each { |a| k[a] = 0 }
    gets; i = 0
    gets.split.each { |a| k.key?(a) ? k[a] += 1 : i += 1 }
    k.each { |c, n| puts "#{c} : #{n}" }
    puts "Invalid : #{i}"
end

HJ93 JAVA 0-1 相等分组

由于 3, 5 必须分在两组, 先把他们分开, 剩下的数字暴力搜到 abs(sum3 - sum5).

#include <iostream>
#include <vector>
using namespace std;
bool dfs(int sumR, int target, vector<int> &v, int index) {
    if (index == v.size()) return sumR == target;
    return dfs(sumR + v[index], target, v, index + 1) ||
           dfs(sumR - v[index], target, v, index + 1);
}
int main() {
    for (int n; cin >> n;) {
        int sum3 = 0, sum5 = 0;
        vector<int> R;
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            if (x % 5 == 0) sum5 += x;
            else if (x % 3 == 0) sum3 += x;
            else R.push_back(x);
        }
        bool result = dfs(0, abs(sum5 - sum3), R, 0);
        cout << boolalpha << result << endl;
    }
}

HJ92 连续最长数字串

while s = gets
    a = s.scan(/\d+/).group_by(&:length)
    n = a.keys.max
    puts "#{a[n].join},#{n}"
end

HJ91 JAVA 2-3 走棋盘方法数

本题如果棋盘是方的, 那么是一个卡特兰数问题, 卡特兰数通式.
不过本题暴力 dfs 即可.

#include <iostream>
using namespace std;
int m, n, count;
void dfs(int i, int j) {
    if (i == m && j == n) count++;
    else {
        if (i < m) dfs(i + 1, j);
        if (j < n) dfs(i, j + 1);
    }
}
int main() {
    while (cin >> m >> n) {
        count = 0;
        dfs(0, 0);
        cout << count << endl;
    }
}

HJ90 合法IP

这题可以很难, 用正则做. (x

while s = gets
    if s.chomp =~ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
        puts 'YES'
    else
        puts 'NO'
    end
end

HJ89 24点

难题, 8做.

HJ88 扑克牌大小

这题的输入用 C++ 比较麻烦, 本身是个简单分类讨论问题.

order = '345678910JQKA2jokerJOKER'
while s = gets
    a, b = s.chomp.split ?-
    c, d = a.split, b.split
    if c.size != d.size
        if [a, b].include? 'joker JOKER'
            puts 'joker JOKER'
        elsif c.size == 4
            puts a
        elsif d.size == 4
            puts b
        else
            puts 'ERROR'
        end
    else
        if order.index(c[0]) > order.index(d[0])
            puts a
        else
            puts b
        end
    end
end

HJ87 密码强度等级

水时间的辣鸡题, 不过我做了.

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
    for (string a; cin >> a;) {
        int score = 0;

        if (a.length() <= 4) score += 5;
        else if (a.length() <= 7) score += 10;
        else score += 25;

        int lower = 0, upper = 0, digit = 0, punct = 0;
        for (char c : a) {
            if (islower(c)) lower++;
            else if (isupper(c)) upper++;
            else if (isdigit(c)) digit++;
            else if (ispunct(c)) punct++;
        }

        if (lower == 0 && upper == 0) score += 0;
        else if (lower * upper == 0) score += 10;
        else score += 20;

        if (digit == 1) score += 10;
        else if (digit > 1) score += 20;

        if (punct == 1) score += 10;
        else if (punct > 1) score += 25;

        int alpha = lower + upper;
        if (lower && upper && digit && punct) score += 5;
        else if (alpha && digit && punct) score += 3;
        else if (alpha && digit) score += 2;

        string result;
        if (score >= 90)
            result = "VERY_SECURE";
        else if (score >= 80)
            result = "SECURE";
        else if (score >= 70)
            result = "VERY_STRONG";
        else if (score >= 60)
            result = "STRONG";
        else if (score >= 50)
            result = "AVERAGE";
        else if (score >= 25)
            result = "WEAK";
        else
            result = "VERY_WEAK";

        cout << result << endl;
    }
}

HJ86 最大连续bit数

本身应该是个位运算魔法题, 想不到魔法就暴力吧.

while i = gets
    p i.to_i.to_s(2).scan(/1+/).map(&:size).max || 0
end

HJ85 最大回文子串长度

本题暴力可做, 但是复杂度是平方, 或者可以看成和逆序的最大公共子串问题.

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int lcs(string a, string b) {
    if (a.length() > b.length()) swap(a, b);
    int m = 0, s = 0;
    vector<vector<int>> dp(a.length() + 1, vector<int>(b.length() + 1, 0));
    for (int i = 1; i <= a.length(); ++i)
        for (int j = 1; j <= b.length(); ++j)
            if (a[i - 1] == b[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                if (dp[i][j] > m) {
                    m = dp[i][j];
                    s = i - m;
                }
            }
    return m;
}
int main() {
    for (string a, b; cin >> a;) {
        b = a; reverse(b.begin(), b.end());
        cout << lcs(a, b) << endl;
    }
    return 0;
}

HJ84 大写字母个数

while s = gets
    p s.scan(/[A-Z]/).size
end

HJ83 二维数组操作

本质维护俩数, 辣鸡题, 8做.

HJ82 分解埃及分数

辣鸡题, bug 多, 8做.

HJ81 字符串匹配

while s = gets and l = gets
    p (s.delete l).empty?
end

HJ80 整型数组合并

排序 + 去重, HJ101 加一行即可.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define all(v) begin(v), end(v)
int main() {
    for (int n, m; cin >> n;) {
        vector<int> v(n);
        for (int i = 0; i < n; ++i) cin >> v[i];
        cin >> m; v.resize(n + m);
        for (int i = 0; i < m; ++i) cin >> v[n + i];
        sort(all(v));
        auto last = unique(all(v));
        v.erase(last, v.end());
        for (int x : v) cout << x;
        cout << endl;
    }
}

HJ79 编辑距离

设 f(a, b) 是字符串 a, b 的最短编辑距离, 显然有

f("", b) = b.size
f(a, "") = a.size
f("aa", "ba") = f("a", "b") # 最后一个字符相同
f("aa", "ab") = [           # 最后一个字符不同
    f("a" , "ab") + 1,
    f("aa", "a" ) + 1,
    f("a" , "a" ) + 1,
].min

套一个记忆化搜索.

#include <map>
#include <tuple>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
string a, b;
map<tuple<int, int>, int> dp;
int f(int i, int j) {
    if (i == -1) return j + 1;
    if (j == -1) return i + 1;
    auto t = make_tuple(i, j);
    auto iter = dp.find(t);
    if (iter != dp.end()) return iter->second;
    int result;
    if (a[size_t(i)] == b[size_t(j)]) result = f(i - 1, j - 1);
    else result = min(min(f(i, j - 1) + 1, f(i - 1, j) + 1), f(i - 1, j - 1) + 1);
    return dp[t] = result;
}
int main() {
    while (cin >> a >> b) {
        dp.clear();
        int result = f(int(a.length() - 1), int(b.length() - 1));
        if (result <= 0)
            cout << 1 << endl;
        else
            cout << "1/" << (result + 1) << endl;
    }
    return 0;
}

HJ78 大整数加法

本来是手动模拟进位, 不过可以用 ruby 我就不客气了.

while a = gets and b = gets
    p a.to_i + b.to_i
end

HJ77 火车进站

全排列 + 判断是否由序列生成.

#include <stack>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
#define all(r) begin(r), end(r)
bool is_pushpop(vector<int> a, vector<int> v) {
    size_t j = 0;
    stack<int> s;
    for (int i : a) {
        s.push(i);
        while (j < v.size() && !s.empty() && v[j] == s.top())
            s.pop(), ++j;
    }
    return s.empty();
}
int main() {
    for (int n; cin >> n;) {
        vector<int> v, a;
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            v.push_back(x);
        }
        a = v;
        sort(all(v));
        vector<vector<int>> w;
        do {
            if (is_pushpop(a, v)) {
                copy(all(v), ostream_iterator<int>(cout, " "));
                cout << endl;
            }
        } while (next_permutation(all(v)));
    }
    return 0;
}

HJ76 尼科彻斯定理

等差数列求和的结果一般是一个二次多项式, 可以猜测三次多项式是由二次多项式数列求和得来. 观察规律, 整数 m 的立方的对应数列第一个是 m * m - m + 1, 数列有 m 个元素.

f = -> x { x * x - x + 1 }
while a = gets
    a = a.to_i
    b = f[a]
    c = Array.new(a) { |i| b + i * 2 }
    puts c.join('+')
end

HJ75 最长公共字串

#include <vector>
#include <string>
#include <iostream>
using namespace std;
int lcs(string a, string b) {
    if (a.length() > b.length()) swap(a, b);
    int m = 0, s = 0;
    vector<vector<int>> dp(a.length() + 1, vector<int>(b.length() + 1, 0));
    for (int i = 1; i <= a.length(); ++i)
        for (int j = 1; j <= b.length(); ++j)
            if (a[i - 1] == b[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                if (dp[i][j] > m) {
                    m = dp[i][j];
                    s = i - m;
                }
            }
    return m;
}
int main() {
    string a, b;
    while (cin >> a >> b) {
        cout << lcs(a, b) << endl;
    }
    return 0;
}

HJ74 参数解析

特殊处理一下双引号, 剩下的按空格分隔.

while s = gets
    s.chomp!
    result = []
    while i = s.index(/[" ]/)
        result << s.slice!(0, i)
        if s[0] == ?"
            s.slice!(0)
            result << s.slice!(0, s.index(?"))
            s.slice!(0)
        end
        s.lstrip!
    end
    result << s unless s.empty?
    puts result.size, result
end

HJ73 日期到天数

#include <iostream>
using namespace std;
bool isleap(int year) {
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
int main() {
    int mm[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    for (int y, m, d, c; cin >> y >> m >> d; ) {
        c = 0;
        for (int i = 1; i < m; ++i) {
            c += mm[i];
            if (i == 2 && isleap(y)) c++;
        }
        cout << c + d << endl;
    }
}

HJ72 百钱买百鸡

#include <iostream>
using namespace std;
int main() {
    for (int _; cin >> _;) {
        for (int i = 0; i < 14; ++i) {
            for (int j = 0; j <= 25; ++j) {
                if (7 * i + 4 * j == 100) {
                    int k = 100 - i - j;
                    cout << i << " " << j << " " << k << endl;
                }
            }
        }
    }
}

HJ71 字符串通配符

可以简单转为正则匹配.

while w = gets and s = gets
    escaped = Regexp.escape(w.chomp).gsub('\?', '.').gsub('\*','.*?')
    r = Regexp.new "^#{escaped}$"
    p !!r.match(s.chomp)
end

HJ70 矩阵乘法计算量

还好不是问最小计算量. ;w; 本质带括号的计算器问题, 加一个栈即可.

while n = gets
    n = n.to_i
    rc = n.times.map { gets.split.map(&:to_i) }
    env = [[]]
    result = 0
    a = gets.chomp
    a.each_char do |c|
        case c
        when '(' then env.push []
        when ')' then
            next if env.size == 1
            a, b = env.pop
            result += a[0] * a[1] * b[1]
            env[-1].push [a[0], b[1]]
        when 'A'..'Z' then
            i = c.ord - 'A'.ord
            env[-1].push rc[i]
        end
    end
    p result
end

HJ69 矩阵乘法

#include <iostream>
using namespace std;
void solve(int x, int y, int z) {
    int a[x][y], b[y][z]; // C++ 并不支持 VLA, 不过 vector 有点长.
    for (int i = 0; i < x; ++i)
        for (int j = 0; j < y; ++j)
            cin >> a[i][j];
    for (int i = 0; i < y; ++i)
        for (int j = 0; j < z; ++j)
            cin >> b[i][j];
    for (int i = 0; i < x; ++i) {
        for (int j = 0; j < z; ++j) {
            int c = 0;
            for (int k = 0; k < y; ++k) {
                c += a[i][k] * b[k][j];
            }
            cout << c << " ";
        }
        cout << endl;
    }
}
int main() {
    for (int x, y, z; cin >> x >> y >> z;) {
        solve(x, y, z);
    }
}

HJ68 成绩排序

还记得看到字符想到 int count[128] 吗, 这里也是, 一共只有 0..100 种成绩.

#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
    int n, t;
    string name;
    while (cin >> n >> t) {
        vector<string> names[101];
        for (int i = 0, g; i < n; ++i) {
            cin >> name >> g;
            names[g].push_back(name);
        }
        for (int i = t ? 0 : 100; t ? i <= 100 : i >= 0; t ? ++i : --i)
            for (name : names[i])
                cout << name << " " << i << endl;
    }
}

HJ67 24点

上面 24 点没做, 这道比较容易, 可以暴搜 3 个运算符的全排列, 利用栈计算器求解.

#include <iostream>
using namespace std;
int seq[7]; // 7 2 + 1 - 10 *
double target = 24.0;
bool calc() {
    int top = 0;
    double stack[4];
    for (int i = 0; i < 7; ++i) {
        if (1 <= seq[i] && seq[i] <= 10) stack[top++] = seq[i];
        else {
            if (top < 2) return false;
            double a = stack[--top], b = stack[--top];
            if (seq[i] == '+') stack[top++] = a + b;
            if (seq[i] == '-') stack[top++] = a - b;
            if (seq[i] == '*') stack[top++] = a * b;
            if (seq[i] == '/') {
                if (b > 0) stack[top++] = a / b;
                else return false;
            }
        }
    }
    return top == 1 && stack[0] == target;
}
bool permute(int start, int end) {
    if (start == end) return calc();
    else {
        for (int i = start; i < end; ++i) {
            swap(seq[start], seq[i]);
            if (permute(start + 1, end)) return true;
            swap(seq[start], seq[i]);
        }
    }
    return false;
}
bool solve() {
    char op[] = "+-*/";
    for (int i = 0; i < 4; ++i) {
        for (int j = 0, k = 4; j < 4; ++j) {
            if (j == i) continue;
            seq[k++] = op[j];
        }
        if (permute(0, 7)) return true;
    }
    return false;
}
int main() {
    while (cin >> seq[0] >> seq[1] >> seq[2] >> seq[3]) {
        cout << boolalpha << solve() << endl;
    }
    return 0;
}

HJ66 配置文件恢复

commands = (<<-F).lines.map { |e| a, b = e.split(?\t); [a.split, b.chomp] }
reset    reset what
reset board    board fault
board add    where to add
board delet    no board at all
reboot backplane    impossible
backplane abort    install first
F
unknown = 'unkown command'
while s = gets
    s = s.split
    w = commands.select { |a, b| a.size == s.size and a.zip(s).all? { |x, y| x.start_with? y } }
    if w.size == 1
        puts w[0][1]
    else
        puts unknown
    end
end

HJ65 最长公共字串

HJ75 改几个字.

#include <vector>
#include <string>
#include <iostream>
using namespace std;
string lcs(string a, string b) {
    if (a.length() > b.length()) swap(a, b);
    int m = 0, s = 0;
    vector<vector<int>> dp(a.length() + 1, vector<int>(b.length() + 1, 0));
    for (int i = 1; i <= a.length(); ++i)
        for (int j = 1; j <= b.length(); ++j)
            if (a[i - 1] == b[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                if (dp[i][j] > m) {
                    m = dp[i][j];
                    s = i - m;
                }
            }
    return a.substr(s, m);
}
int main() {
    string a, b;
    while (cin >> a >> b) {
        cout << lcs(a, b) << endl;
    }
    return 0;
}

HJ64 MP3光标位置

#include <iostream>
using namespace std;
int main() {
    for (int n; cin >> n;) {
        int l = 0, cur = 0;
        string s;
        cin >> s;
        for (char c : s) {
            if (n > 4) {
                if (c == 'U' && l == cur) l--;
                if (c == 'D' && l + 3 == cur) l++;
                if (l < 0) l = n - 4;
                else if (l + 3 >= n) l = 0;
            }
            if (c == 'U') cur = (cur + n - 1) % n;
            else if (c == 'D') cur = (cur + 1) % n;
        }
        for (int i = l + 1, end = l + (n > 4 ? 4 : n); i <= end; ++i) {
            cout << i;
            if (i < end) cout << " ";
            else cout << endl;
        }
        cout << cur + 1 << endl;
    }
    return 0;
}

HJ63 DNA序列

calc = -> s { s.chars.count { |c| c == 'G' || c == 'C' }.fdiv s.size }
while s = gets
    s.chomp!
    n = gets.to_i
    i = (0..(s.size - n)).max_by { |i| calc[s[i, n]] }
    puts s[i, n]
end

HJ62 二进制1的个数

经典位运算: i &= i - 1 可以清掉 i 末尾的一个 1.

#include <iostream>
using namespace std;
#define all(r) begin(r), end(r)
int main() {
    for (int i, k; cin >> i;) {
        for (k = 0; i; i &= i - 1)
            k++;
        cout << k << endl;
    }
    return 0;
}

HJ61 放苹果

把 m 个苹果放在 n 个盘子里, 有以下几种情况:

f(m, n) =
    m = 0 && n = 1 => 1
    m < n          => f(m, m)
    _              => f(m, n - 1) + f(m - n, n)
  1. 1 个盘子, 0 个苹果 -> 1 种方法
  2. 盘子比苹果多, 等价于盘子和苹果一样多时的方法
  3. (苹果比盘子多), 等于 n-1 个盘子的方法加每个盘子都放一个之后的方法
#include <iostream>
using namespace std;
#define all(r) begin(r), end(r)
int f(int a, int b) {
    if (a == 0 || b == 1)
        return 1;
    if (b > a)
        return f(a, a);
    else
        return f(a, b - 1) + f(a - b, b);
}
int main() {
    for (int a, b; cin >> a >> b;) {
        cout << f(a, b) << endl;
    }
    return 0;
}

HJ60 最接近的素数和

从中间往两边找.

require 'prime'
while a = gets
    a = a.to_i
    i = a / 2
    until i.zero?
        j = a - i
        if i.prime? and j.prime?
            p i, j
            break
        end
        i -= 1
    end
end

HJ59 找出字符串中第一个只出现一次的字符

while s = gets
    a = s.chomp.chars.group_by(&:itself)
    if a.each { |k, v| if v.size == 1 then break puts k end }
        p -1
    end
end

HJ58 输入n个整数,输出其中最小的k个

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
#define all(r) begin(r), end(r)
int main() {
    for (int n, m; cin >> n >> m;) {
        vector<int> v(n);
        for (int i = 0; i < n; ++i) {
            cin >> v[i];
        }
        sort(all(v));
        copy(v.begin(), v.begin() + m, ostream_iterator<int>(cout, " "));
        cout << endl;
    }
    return 0;
}

HJ57 无线OSS-高精度整数加法

手打大整数进位/借位的话, 这题就长了.

while a = gets and b = gets
    p a.to_i + b.to_i
end

HJ56 iNOC产品部--完全数计算

def count n
    ret = 0
    2.upto(n) { |i|
      sum = 1
      2.upto(Math.sqrt(i)) { |j|
        if i % j == 0
            sum += j
            sum += i / j if i / j != j
        end
      }
      ret += 1 if sum == i
    }
    ret
end
while s = gets
    p count s.to_i
end

HJ55 (练习用)挑7

#include <iostream>
using namespace std;
int main() {
    int n, a;
    while (cin >> n) {
        a = 0;
        for (int i = 1; i <= n; ++i)
            if (i % 7 == 0) a++;
            else {
                int t = i;
                do {
                    if (t % 10 == 7) {
                        a++;
                        break;
                    }
                } while (t /= 10);
            }
        cout << a << endl;
    }
}

HJ54 表达式求值

p eval gets

HJ53 iNOC产品部-杨辉三角的变形

本题暴力也可以过, 不过答案有个规律.

while i = gets
    i = i.to_i
    case
    when i < 3 then p -1
    when i % 2 == 1 then p 2
    when i % 4 == 0 then p 3
    else p 4
    end
end

HJ52 计算字符串的距离

这题出现几次了?

#include <map>
#include <tuple>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
string a, b;
map<tuple<int, int>, int> dp;
int f(int i, int j) {
    if (i == -1) return j + 1;
    if (j == -1) return i + 1;
    auto t = make_tuple(i, j);
    auto iter = dp.find(t);
    if (iter != dp.end()) return iter->second;
    int result;
    if (a[size_t(i)] == b[size_t(j)]) result = f(i - 1, j - 1);
    else result = min(min(f(i, j - 1) + 1, f(i - 1, j) + 1), f(i - 1, j - 1) + 1);
    return dp[t] = result;
}
int main() {
    while (cin >> a >> b) {
        dp.clear();
        cout << f(int(a.length() - 1), int(b.length() - 1)) << endl;
    }
    return 0;
}

HJ51 输出单向链表中倒数第k个结点

打一遍链表太累了, 其实就是原地构造一个(头插法)新链表.

while gets
    v = gets.split
    n = gets.to_i
    if 0 < n and n <= v.size
        puts v[-n]
    else
        p 0
    end
end

HJ50 四则运算

这题创造性地引入了两种新括号, 不过他又没错误输入, 所以直接.

p eval gets.tr('{[', '}]')

HJ49 多线程

其实就是给四个线程加同步锁, 最后一个线程给资源给第一个线程; 8做.

HJ48 从单向链表中删除指定值的节点

本身要构建链表挺麻烦的, 得写个 find_and_insert(p, x) delete_value(x).

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define all(v) begin(v), end(v)
int main() {
    for (int n; cin >> n;) {
        vector<int> v(1);
        cin >> v[0];
        for (int i = 0, x, y; i < n - 1; ++i) {
            cin >> x >> y;
            v.insert(find(all(v), y) + 1, x);
        }
        cin >> n;
        v.erase(find(all(v), n));
        for (int n : v) cout << n << " "; cout << endl;
    }
}

HJ47 线性插值

没看懂题, 算了8.

HJ46 按字节截取字符串

这题还就只有部分语言能做.

#include <string>
#include <iostream>
using namespace std;
int main() {
    for (string s; cin >> s;) {
        int n; cin >> n;
        cout << s.substr(0, n) << endl;
    }
}

HJ45 名字的漂亮度

类似霍夫曼编码一开始要将频率高的字分配比较高的权重.

d = [*1..26].reverse
while s = gets
    s.to_i.times {
        a = gets.chomp.chars.group_by(&:itself)
        b = a.values.map(&:count).sort.reverse.zip(d)
        c = b.map { |e| e.reduce :* }.reduce :+
        p c
    }
end

HJ44 Sudoku-Java

简单 dfs, 稍微压一下行.

#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
#define all(v) begin(v), end(v)
#define F(i,a,z) for (int i = a; i < z; ++i)
#define G(k,i) F(k, i / 3 * 3, i / 3 * 3 + 3)
#define P(v) copy(all(v), ostream_iterator<int>(cout, " ")), cout << endl
int a[9][9];
void print() { F(i, 0, 9) P(a[i]); }
bool check(int i, int j, int x) {
    F(k, 0, 9) if (a[i][k] == x || a[k][j] == x) return false;
    G(k, i) G(l, j) if (a[k][l] == x) return false;
    return true;
}
bool dfs(int k) {
    if (k == 81) return print(), true;
    int i = k / 9, j = k % 9;
    if (a[i][j] > 0) return dfs(k + 1);
    F(x, 1, 10) if (check(i, j, x)) {
        a[i][j] = x; if (dfs(k + 1)) return true;
        a[i][j] = 0;
    } return false;
}
int main() {
    F(i, 0, 9) F(j, 0, 9) cin >> a[i][j];
    dfs(0);
}

HJ43 迷宫问题

寻路当然是 A* 啦.

#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
using namespace std;
#define F(i,a,z) for (int i = a; i < z; ++i)
#define T(k,l,b) do { if (passable(k, l)) p[k][l] = b, q.push({ k, l, step + 1 }); } while (0)
#define P(b,k,l) do { if (p[i][j] == b) print(k, l); } while (0)
int m, n, a[10][10], p[10][10]; // a 迷宫, p 每个点的上一个点在哪(用小键盘标方向)
struct pt { int i, j, k; int h() const { return i + j + k; } };
bool passable(int i, int j) {
    return 0 <= i && i <= m && 0 <= j && j <= n && a[i][j] == 0 && p[i][j] == 0;
}
void print(int i, int j) {
    P(2, i + 1, j); P(4, i, j - 1); P(6, i, j + 1); P(8, i - 1, j);
    printf("(%d,%d)\n", i, j);
}
int main() {
    auto cmp = [](const pt &a, const pt &b) { return a.h() > b.h(); };
    while (cin >> m >> n) {
        F(i, 0, m) F(j, 0, n) cin >> a[i][j], p[i][j] = 0;
        priority_queue<pt, vector<pt>, decltype(cmp)> q(cmp);
        q.push({ 0, 0, 0 }); a[0][0] = 1; // 标记起点不可通行
        for (int i, j, step; !q.empty();) {
            auto pt = q.top(); q.pop(); i = pt.i, j = pt.j, step = pt.k;
            T(i - 1, j, 2); T(i, j + 1, 4); T(i, j - 1, 6); T(i + 1, j, 8);
        }
        print(m - 1, n - 1);
    }
}

HJ42 学英语

辣鸡题, 8做.

HJ41 称砝码

希尔伯特空间展开.

#include <set>
#include <vector>
#include <iostream>
using namespace std;
int main() {
    for (int n; cin >> n;) {
        set<int> s, t;
        vector<int> a, v;
        a.resize(size_t(n));
        for (int i = 0; i < n; ++i) cin >> a[size_t(i)]; 
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            for (int j = 0; j < x; ++j) v.push_back(a[size_t(i)]);
        }
        s.insert(0);
        for (int x : v) {
            t = s;
            for (int y : t) s.insert(x + y);
        }
        cout << s.size() << endl;
    }
    return 0;
}

HJ40 输入一行字符,分别统计出包含英文字母、空格、数字和其它字符的个数

建议大家都去用 ctype.h 里的东西.

#include <cctype>
#include <string>
#include <iostream>
using namespace std;
int main() {
    string ss;
    while (getline(cin, ss)) {
        int a = 0, s = 0, d = 0, o = 0;
        for (char c : ss) {
            if (isalpha(c)) a++;
            else if (isspace(c)) s++;
            else if (isdigit(c)) d++;
            else o++;
        }
        cout << a << endl << s << endl << d << endl << o << endl;
    }
    return 0;
}

HJ39 判断两个IP是否属于同一子网

相同子网的 IP & 掩码 相同.

#include <iostream>
#include <cstdio>
using namespace std;
int ip1[4], ip2[4], ip3[4];
int comp() {
    for (int i = 0; i < 4; ++i) {
        if (!(0 <= ip1[i] && ip1[i] <= 255 &&
              0 <= ip2[i] && ip2[i] <= 255 &&
              0 <= ip3[i] && ip3[i] <= 255))
            return 1;
        if ((ip1[i] & ip3[i]) != (ip2[i] & ip3[i]))
            return 2;
    }
    return 0;
}
int main() {
    while (true) {
        if (scanf("%d.%d.%d.%d", &ip1[0], &ip1[1], &ip1[2], &ip1[3]) != 4) break;
        if (scanf("%d.%d.%d.%d", &ip2[0], &ip2[1], &ip2[2], &ip2[3]) != 4) break;
        if (scanf("%d.%d.%d.%d", &ip3[0], &ip3[1], &ip3[2], &ip3[3]) != 4) break;
        cout << comp() << endl;
    }
    return 0;
}

HJ38 求小球落地5次后所经历的路程和第5次反弹的高度

#include <iostream>
#include <cstdio>
using namespace std;
int main() {
    double h;
    while (cin >> h) {
        double sum = 0, h5 = h;
        for (int i = 0; i < 5; ++i) {
            sum += h5;
            h5 /= 2;
            sum += h5;
        }
        sum -= h5;
        printf("%.6f\n%.6f\n", sum, h5);
    }
}

HJ37 统计每个月兔子的总数

老斐波那契了.

#include <stdio.h>
int f[50];
int fib(int i) {
    if (i <= 1) return i;
    if (f[i]) return f[i];
    return f[i] = fib(i - 1) + fib(i - 2);
}
int main() {
    int i;
    f[1] = 1;
    while (scanf("%d", &i) == 1)
        printf("%d\n", fib(i));
}

HJ36 字符串加密

while w = gets and s = gets
    s.chomp!
    w = w.chomp.chars.uniq.join.downcase
    t = [*'a'..'z'].join
    u = w + (t.delete w)
    r = s.downcase.tr t, u
    s.each_char.with_index { |c, i| r[i] = r[i].upcase if c =~ /[A-Z]/ }
    puts r
end

HJ35 蛇形矩阵

可以观察到, 第一行是自然数列的和.

f = Array.new(101) { |i| (1 + i) * i / 2 }
while n = gets
    n = n.to_i
    a = f[1..n]
    puts a.join ' '
    (n - 1).times { puts a.tap(&:shift).map! { |e| e - 1 }.join ' ' }
end

HJ34 图片整理

while s = gets
    puts s.chomp.chars.sort.join
end

HJ33 整数与IP地址间的转换

上面也有一道 IP 题, 不过这题是保证输入正确的, 可以直接瞎搞.

while a = gets and b = gets
    p a.split('.').map(&:to_i).pack('C*').unpack('N')[0]
    puts [b.to_i].pack('N').unpack('C*').join('.')
end

HJ32 【中级】字符串运用-密码截取

最长回文字串, 上面也有, 这里暴力.

def helper s, l, r
    while 0 <= l && r < s.size && s[l] == s[r]
        l -= 1
        r += 1
    end
    return r - l - 1
end
while s = gets
    s.chomp!
    max = 0
    s.size.times { |i| max = [max, helper(s, i, i), helper(s, i, i + 1)].max }
    p max
end

HJ31 【中级】单词倒排

puts gets.tr('^a-zA-Z', ' ').split.reverse.join(' ')

HJ30 字符串合并处理

这种题, 用 ruby 都要方便很多.

rev = -> a { ('%04b' % a).reverse.to_i 2 }
while s = gets
    a = s.split.join.chars.group_by.with_index { |c, i| i.even? }
    b = a.each_value(&:sort!).values.reduce(&:zip).flatten
    c = b.map { |e| e =~ /\h/ ? rev[e.to_i(16)].to_s(16).upcase : e }
    puts c.join
end

HJ29 字符串加解密

while s = gets and u = gets
    puts s.chomp.tr 'a-zA-Z0-9', 'B-ZAb-za1-90'
    puts u.chomp.tr 'B-ZAb-za1-90', 'a-zA-Z0-9'
end

HJ28 素数伴侣

二分图的最大匹配, 匈牙利算法. 简单地说, 对于二分图左侧的每个点, 找一个右侧的点与他匹配,
匹配逻辑是: 对于每个右侧的点, 如果他没有匹配过, 直接选他, 如果他已经匹配了张三, 看看能不能让张三匹配到另一个人(递归).

#include <vector>
#include <iostream>
#define all(v) begin(v), end(v)
using namespace std;
bool primes_not[60005];
bool is_prime(int a) {
    return !primes_not[a];
}
bool dfs(int odd, vector<int> &evens, vector<int> &choose, vector<bool> &visit) {
    for (int i = 0; i < evens.size(); ++i) {
        if (visit[i] || !is_prime(odd + evens[i])) continue;
        visit[i] = true;
        if (choose[i] == 0 || dfs(choose[i], evens, choose, visit)) {
            choose[i] = odd;
            return true;
        }
    }
    return false;
}
int main() {
    primes_not[0] = primes_not[1] = true;
    for (int i = 2; i < 60005; ++i)
        if (!primes_not[i])
            for (int j = i * 2; j < 60005; j += i)
                primes_not[j] = true;
    for (int n; cin >> n;) {
        vector<int> odds, evens;
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            if (x & 1) odds.push_back(x);
            else evens.push_back(x);
        }
        vector<int> choose(evens.size(), 0);
        int result = 0;
        for (int odd : odds) {
            vector<bool> visit(evens.size(), false);
            if (dfs(odd, evens, choose, visit)) result++;
        }
        cout << result << endl;
    }
}

HJ27 查找兄弟单词

while u = gets
    _, *a, w, i = u.split
    i = i.to_i - 1
    s = -> x { x.chars.sort.join }
    e = -> a, b { a != b && s[a] == s[b] }
    c = a.select { |x| e[x, w] }.sort
    p c.size
    puts c[i] if c.size > i
end

HJ26 字符串排序

#include <iostream>
#include <vector>
#include <string>
#include <cctype>
using namespace std;
int main() {
    string s;
    while (getline(cin, s)) {
        size_t len = s.length();
        vector<char> v;
        for (int j = 0; j < 26; ++j) {
            for (size_t i = 0; i < len; ++i) {
                if (isalpha(s[i]) && tolower(s[i]) == 'a' + j) {
                    v.push_back(s[i]);
                }
            }
        }
        for (size_t i = 0, k = 0; i < len && k < v.size(); ++i) {
            if (isalpha(s[i])) s[i] = v[k++];
        }
        cout << s << endl;
    }
}

HJ25 数据分类处理

辣鸡题, 题意说不清.

while s = gets and u = gets
  _, *t = s.split.map(&:to_i)
  _, *r = u.split.map(&:to_i)
  r.sort!.uniq!
  o = []
  r.each { |aa|
    a = aa.to_s
    c = 0
    tt = []
    t.each_with_index { |b, i|
      if b.to_s.include?(a)
        c += 1
        tt << i << b
      end
    }
    tt.unshift(aa, c) if c > 0
    o.push(*tt)
  }
  puts [o.size, *o].join(' ')
end

HJ24 合唱队

分别从两端找最长上升序列, 然后找中间一个点使两侧加起来最大.

#include <iostream>
#include <algorithm>
using namespace std;
int a[10000], dp1[10000], dp2[10000];
int main() {
    int n, m;
    while (cin >> n) {
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
            dp1[i] = 1;
            for (int j = 0; j < i; ++j) {
                if (a[i] > a[j]) {
                    dp1[i] = max(dp1[i], dp1[j] + 1);
                }
            }
        }
        for (int i = n - 1; i >= 0; --i) {
            dp2[i] = 1;
            for (int j = n - 1; j >= i; --j) {
                if (a[i] > a[j]) {
                    dp2[i] = max(dp2[i], dp2[j] + 1);
                }
            }
        }
        m = 0;
        for (int i = 0; i < n; ++i) {
            m = max(m, dp1[i] + dp2[i] - 1);
        }
        cout << n - m << endl;
    }
    return 0;
}

HJ23 删除字符串中出现次数最少的字符

while s = gets
    a = s.chomp.chars.group_by(&:itself)
    a.each { |k, v| a[k] = v.count }
    b = a.values.min
    c = a.keys.select { |k| a[k] == b }
    puts s.delete c.join
end

HJ22 汽水瓶

本身是个递归题,

f(n) = n <  2 ? 0 :
       n == 2 ? 1 :
                n / 3 + f(n / 3 + n % 3)

不过实际上还是个找规律.

#include <stdio.h>
int main() {
    int a;
    while (scanf("%d", &a) == 1) {
        if (a == 0) break;
        printf("%d\n", a / 2);
    }
    return 0;
}

HJ21 简单密码破解

np = [2] * 3 + [3] * 3 + [4] * 3 + [5] * 3 +
     [6] * 3 + [7] * 4 + [8] * 3 + [9] * 4
t = "b-za#{np.join}"
while s = gets
    puts s.tr 'A-Za-z', t
end

HJ20 密码验证合格程序

前面有一个更复杂的密码程序, 8做了.

HJ19 简单错误记录

log = Hash.new { |h, k| h[k] = 0 }
last = -> a, n { a[-n..-1] || a }
STDIN.read.split.each_slice(2) do |f, l|
    f = last[f.split('\\').last, 16]
    log[[f, l]] += 1
end
last[log.to_a, 8].each { |(f, l), c| puts [f, l, c].join(' ') }

HJ18 识别有效的IP地址和掩码并进行分类统计

水时间题, 8做.

HJ17 坐标移动

while s = gets
    pos = [0, 0]
    s.chomp.split(?;).each { |a|
        if m = a.match(/^([ASDW])(\d+)$/)
            n = m[2].to_i
            case m[1]
            when ?A then pos[0] -= n
            when ?S then pos[1] -= n
            when ?D then pos[0] += n
            when ?W then pos[1] += n
            end
        end
    }
    puts pos.join ?,
end

HJ16 购物单

01 背包, 好难, 8做了.

HJ15 求int型数据在内存中存储时1的个数

#include <stdio.h>
int main() {
    int a, i;
    scanf("%d", &a);
    for (i = 0; a; ++i) a &= (a - 1);
    printf("%d\n", i);
}

HJ14 简单排序

while n = gets
    puts n.to_i.times.map { gets.chomp }.sort
end

HJ13 句子逆序

while s = gets
    puts s.split.reverse.join(' ')
end

HJ12 字符串反转

#include <stdio.h>
#include <string.h>
char s[1001];
int main() {
    char t;
    int i, len;
    scanf("%s", s);
    len = strlen(s);
    for (i = 0; i < len / 2; ++i) {
        t = s[i];
        s[i] = s[len - i - 1];
        s[len - i - 1] = t;
    }
    printf("%s\n", s);
}

HJ11 数字颠倒

#include <stdio.h>
int main() {
    int a, i = 0;
    char s[20];
    scanf("%d", &a);
    do {
        s[i++] = '0' + (a % 10);
    } while (a /= 10);
    s[i] = '\0';
    printf("%s\n", s);
}

HJ10 字符个数统计

while s = gets
    p s.chomp.chars.uniq.size
end

HJ9 提取不重复的整数

while s = gets
    puts s.chomp.reverse.chars.uniq.join
end

HJ8 合并表记录

while s = gets
    h = Hash.new { |h, k| h[k] = 0 }
    s.to_i.times { k, v = gets.split.map(&:to_i); h[k] += v }
    h.sort_by { |k, v| k }.each { |k, v| puts "#{k} #{v}" }
end

HJ7 取近似值

#include <stdio.h>
int main() {
    double a;
    scanf("%lf", &a);
    printf("%d\n", (int)(a + 0.5));
}

HJ6 质数因子

#include <iostream>
using namespace std;
int main() {
    long a;
    cin >> a;
    for (int i = 2; a > 1; ++i)
        while (a % i == 0) {
            cout << i << " ";
            a /= i;
        }
}

HJ5 进制转换

对了, C++ 可以用 cin >> hex >> i.

while s = gets
    p eval s
end

HJ4 字符串分隔

#include <stdio.h>
#include <string.h>
int main() {
    char s[20];
    while (scanf("%8s", s) == 1) {
        printf("%s", s);
        for (size_t i = 0; i < 8 - strlen(s); ++i)
            printf("0");
        printf("\n");
    }
}

HJ3 明明的随机数

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
    for (int n; cin >> n;) {
        vector<int> v;
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            v.push_back(x);
        }
        sort(v.begin(), v.end());
        auto last = unique(v.begin(), v.end());
        v.erase(last, v.end());
        for (int x : v)
            cout << x << endl;
    }
}

HJ2 计算字符个数

#include <string>
#include <iostream>
#include <cctype>
using namespace std;
int main() {
    int i = 0;
    string s, a;
    getline(cin, s);
    cin >> a;
    for (char c : s)
        if (toupper(c) == toupper(a[0]))
            i++;
    cout << i << endl;
}

HJ1 字符串最后一个单词的长度

#include <string>
#include <iostream>
using namespace std;
int main() {
    string a;
    while (cin >> a)
        ;
    cout << a.length() << endl;
}

最近做的, 模拟内存分配, 别看写得长, 其实没任何算法在里面.

mem = { 0 => 100 } # 下标 => 正数=可用空间, 负数=正在占用

free = -> i { # 查询 i 位置可用空间
  j = mem.keys.select { |j| j + mem[j] >= i && j <= i }.min
  return 0 if j.nil?
  return j + mem[j] - i
}

req = -> n { # 申请大小为 n 的内存
  return if n.zero?
  i = (0..99).find { |i| free[i] >= n } # 暴力找到一个可用位置, 可以优化
  return if i.nil?
  m = mem[i]
  mem[i] = -n
  mem[i + n] = m - n if m > n
  return i
}

rar = -> { # 整理连续空内存
  100.times { |i| # 暴力, 可以优化成自动跳过内存区间.
    next if not mem.key? i or mem[i] < 0
    loop {
      j = i + mem[i]
      break if not mem.key? j or mem[j] < 0
      mem[i] += mem[j]
      mem.delete j
    }
  }
}

rel = -> i { # 释放 i 位置的空间, 题目保证 i 是 mem 的下标
  return if not mem.key? i or mem[i] > 0
  mem[i] = -mem[i]
  rar.call
  return true
}

while n = gets
  n.to_i.times {
    a, b = gets.split('=')
    b = b.to_i
    if a == 'REQUEST'
      if k = req[b]
        p k
      else
        puts 'error'
      end
    else
      if not rel[b]
        puts 'error'
      end
    end
  }
end

以上.