T1:Cow College

总学费 \(=\) 设置的单人学费 \(\times\) 接受的奶牛数

一旦固定单人学费,就能确定接受的奶牛数

单人学费可以是哪些值?

\(\{c_1, c_2, \cdots, c_n\}\) 其中之一作为学费门槛

暴力做法是先枚举单人学费是多少,再查看每个人的可接受学费是否大于等于单人学费,复杂度为 \(O(n^2)\)

可以通过对序列 \(c\) 做降序排序来优化掉第二重循环

代码实现
n = int(input())
c = list(map(int, input().split()))

c.sort(reverse=True)

tot, ans = 0, 0
for i in range(n):
    if (i+1)*c[i] >= tot:
        tot = (i+1)*c[i]
        ans = c[i]
        
print(tot, ans)

T2:Feeding the Cows B

Saruman's Army

对每头 G,尽可能往右放 G 草地
对每头 H,允许(若越界)回过头往左找空地
可能带来的问题:

  • 找不到
  • 时间复杂度
  • 整体最优解
  1. 找不到---无解
    \(k=0\),原地待命
    \(k \geqslant 1\),种草稀疏

  2. 时间复杂度
    \(O(n)\)
    从左回头找最多只会出现一次

  3. 最优解

关键:G/H 草地不会互相抢占位置

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

void solve() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    int cnt = 0;
    string ans = string(n, '.');
    // G
    rep(i, n) {
        if (s[i] == 'H') continue;
        int pos = min(i+k, n-1);
        ans[pos] = 'G';
        ++cnt;
        i = pos+k;
    }
    // H
    rep(i, n) {
        if (s[i] == 'G') continue;
        int pos = min(i+k, n-1);
        while (ans[pos] != '.') --pos;
        ans[pos] = 'H';
        ++cnt;
        i = pos+k;
    }
    cout << cnt << '\n' << ans << '\n';
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}

T3:Reverse Engineering

程序分为有条件返回和无条件返回

有条件返回形如

if (b[i] == x)
    return y;

\(x, y \in \{0, 1\}\)

标准化(normalize),对于可能性很多的复杂性事物,我们可以人为地制定一些规则使其有统一格式

  1. 每一变量只判断一次
  2. 程序一定可以写成 \(n+1\)

算法:

  1. 寻找一列 \(i\),枚举 \(x, y \in \{0, 1\}\),判断能否有 if (b[i] == x) return y;
  2. 如果找到,则筛去 \(b_i=x\) 的行,并筛去列 \(i\)
    如果找不到,判断:若所有输入都返回,则输出 OK,否则矛盾,输出 LTE
  3. 回到步骤 \(1\),总共 \(n\)
  4. 最后判断能否无条件返回 \(0/1\)

总时间复杂度为:\(O(n^2m)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

bool vr[105], vc[105];

void solve() {
    memset(vr, false, sizeof vr);
    memset(vc, false, sizeof vc);
    
    int n, m;
    cin >> n >> m;
    vector<string> s(m);
    vector<int> val(m);
    rep(i, m) cin >> s[i] >> val[i];
    
    auto Return = [&](int id, int x, int y) {
        rep(i, m) {
            if (vr[i]) continue;
            if (s[i][id]-'0' == x) {
                if (val[i] != y) return false;
            }
        }
        return true;
    };
    auto Return2 = [&](int y) {
        rep(i, m) {
            if (vr[i]) continue;
            if (val[i] != y) return false;
        }
        return true;
    };
    auto ok = [&]{
        rep(i, n) {
            bool found = false;
            // 判断是否有 if (b[j] == x) return y; 这一程序语句是否成立 -> Return(j, x, y)
            rep(j, n) if (!found) {
                rep(x, 2) if (!found) {
                    rep(y, 2) if (!found) {
                        if (!vc[j] and Return(j, x, y)) {
                            found = vc[j] = true;
                            rep(k, m) {
                                // 这一输入不能因为之前的程序语句就结束了,因此这一行要未被筛选过
                                if (!vr[k] and s[k][j]-'0' == x) {
                                    vr[k] = true;
                                }
                            }
                        }
                    }
                }
            }
            if (!found) break;
        }
        return Return2(0) or Return2(1);
    }();
    
    if (ok) puts("OK");
    else puts("LIE");
}

int main() {
    int t;
    cin >> t;
    
    while (t--) solve();
    
    return 0;
}