ZZULI 春季acm选拔赛 (三)

Z Z U L I 春季 a c m 选拔赛 ( 三 ) ZZULI 春季acm选拔赛 (三) ZZULI春季acm选拔赛()

  • 以下所有代码都只提供核心部分 (由于代码是之后重新打的, 细节错了的话可以提醒我改一下
  • 由于上次的宏定义太多了, 有同学反应看不懂, 这次就全都去掉了
  • 看不懂的语法记得先查一下, 查不到的或者有疑问的可以问我

7-1 字符串连接

  • 因为 单词1单词 2 各自含有的单词不重复, 即从 单词1单词2中任意选一对, 都能组成不同的语言 即答案为 n * m
void solve() {
	int n, m;
	cin >> n >> m;
	
	string s;
	// 由于是多实例, 记得把所有的输入读完, 否则会影响下一次读入
	for (int i = 1; i <= n + m; i ++) cin >> s;
	
	cout << n * m;
}

7-2 回文素数

  • n循环到m逐个判断即可, 但由于判断素数需要 O ( n ) O(\sqrt{n}) % O(n )的复杂度, 而判断回文大概只需要 O ( l e n ( n ) ) O(len(n)) O(len(n))的复杂度
  • 故我们先判断该数是否回文, 再判断该数是否是素数
bool isH(int x) {
    string s = to_string(x);  // c++ 11之后才支持该函数
    string t = s;
    reverse(s.begin(), s.end());
    return s == t;
}

bool isP(int x) {
    // 由于 x 至少是 3 位数, 不再特判 1
    for (int i = 2; i <= x / i; i ++)
        if (x % i == 0)
            return false;

    return true;
}

void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> ans;
    for (int i = n; i <= m; i ++)
        if (isH(i))
            if (isP(i))
                ans.push_back(i);
    
    cout << ans.size() << '\n';
    for (auto x: ans) cout << x << "\n";
}
 

7-3 轰炸

  • 一眼差分, 再看一眼就不会了, 结果直接暴力就可以过 (hh
  • 轰炸次数暴力累加即可, 开个vis数组存一下(x, y)最后一次轰炸的编号即可
const int N = 1000 + 10;
int n, m, k, q;
int a[N][N];
int vis[N][N];


void solve() {
    cin >> n >> m >> k >> q;
    for (int c = 1; c <= k; c ++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int i = x1; i <= x2; i ++)
            for (int j = y1; j <= y2; j ++)
                a[i][j] ++, vis[i][j] = c;
    }

    while (q --) {
        int x, y;
        cin >> x >> y;
        if (a[x][y]) cout << "Y " << a[x][y] << " " << vis[x][y] << "\n";
        else cout << "N\n";
    }
}

7-4 溢出控制

  • 没想到现在还有人考高精度 (不是很懂
  • 题目已经把答案说的很明显了, 答案是 2 8 ∗ m − 1 − 1 2^{8*m - 1} -1 %power 28m11, 但这个数很大, 需要用高精度乘法来算, 不知道double会不会溢出, 没试, 各位可以试一下
  • 也可以直接用 python
vector<int> mul(vector<int> A, int b) {  // 我的高精度乘法的模板, 看不懂的可以上网学一下
    vector<int> C;

    int t = 0;    
    for (int i = 0; i < A.size(); i ++) {
        int x = A[i] * b + t;
        C.push_back(x % 10);
        t = x / 10;
    }
    
    while (t) C.push_back(t % 10), t /= 10;

    return C;
}

void solve() {
	int m;
    cin >> m;
    int n = m * 8 - 1;  // 2的幂次
    
    vector<int> ans(1, 1);
    for (int i = 1; i <= n; i ++) ans = mul(ans, 2);
    ans[0] --; // 2的任何次方最后一位都不为 0, 即使 -1, 也不会从前面借位
    
    reverse(ans.begin(), ans.end());  // 答案是倒序存的, 需要反过来输出

    for (auto x: ans) cout << x;
    cout << "\n"; // 多实例记得换行
}
  • py还是不太会用, 查了半天 (晕
# 方法 1
import sys
for n in sys.stdin:
    m = int(n) * 8 - 1
    print(2 ** m - 1)
    
# 方法 2
while True:
    try:
        n = input()
        m = int(n) * 8 - 1
        print(2 ** m - 1)
    except:
        break

7-5 高空坠球

  • 模拟一下即可
  • 除了第一次落下来总高度加上 h, 以后每一次落地加的都是2倍的 h
void solve() {
    int k;
    double h, sum = 0;
    cin >> h >> k;
    if (k) sum += h;  // 特判k == 0的情况
    else h = 0;
    
    h /= 2;
    for (int i = 1; i < k; i ++) sum += 2 * h, h /= 2;
    printf("%.1f %.1f", sum, h);
}

7-6 Keven的字符串

  • 典型贪心问题 (时间原因我不再证明, 感兴趣可以查一下贪心之数字拼接
  • 结论: 按特殊规则(有的难描述, 看代码吧)排序之后, 直接拼接就是答案
bool cmp(string a, string b) {
	return a + b < b + a;
}

void solve() {
    cin >> n;
    vector<string> s1(n);
    for (auto& s: s1) cin >> s;
    // 方法 1
    sort(s1.begin(), s1.end(), [&](string a, string b) {  // 不会写lambda的可以手写个cmp函数, 见下面的sort
        return a + b < b + a;
    });
    // 方法 2
    sort(s1.begin(), s1.end(), cmp);
    for (auto s: s1) cout << s;
}

7-7 放文件

  • 考虑贪心, 模拟一下就行 (看数据范围应该是要开long long的, 我的已经 #define int long long
  • 开个c数组来存放a[i] - b[i]的值, 即压缩 i 可以获得的空间, 对 c 从大到小排序, 依次判断即可
const int N = 1e5 + 10;
int n, m;
int a[N], b[N], c[N];
void solve() {
    int sum = 0;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i] >> b[i];
        sum += a[i];
        c[i] = a[i] - b[i];
    }
    sort(c + 1, c + n + 1, greater<int>());

    for (int i = 0; i <= n; i ++) {  // 从 0 开始判断, 有可能不需要压缩
        if (sum - c[i] <= m) {
            cout << i;
            return;
        }
        sum -= c[i];
    }
    cout << "-1";  // 如果最后还没成功, 就输出 -1
}

7-8 编辑距离问题

  • 字符串DP典题
  • 由于时间有限, 不再放思路, 对这题感兴趣的可以私下找我聊
const int N = 2000 + 10;
int n, m;
int f[N][N];

void solve() {
    string a, b;
    cin >> a >> b;
    
    n = a.size(), m = b.size();
    a = " " + a; b = " " + b;  // 让字符串下标从 1 开始, 方便判断
    
    memset(f, 0x3f, sizeof f);
   
    for (int i = 0; i <= n; i ++) f[i][0] = i;
    for (int i = 0; i <= m; i ++) f[0][i] = i;
    
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++) {
            if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1];
            else f[i][j] = min({f[i][j - 1] + 1, f[i - 1][j] + 1, f[i - 1][j - 1] + 1});
        }
    
    cout << f[n][m];
}

7-9 模拟炸弹人

  • 考察 flood fill 算法, 又名洪水灌溉算法
  • (感觉并查集也行, 有时间的同学可以试试
  • DFSBFS 都可以实现
  • 更新一下, 测试发现DFS会爆空间, 1000 * 1000 的矩阵如果全是 * 那么应该是会爆栈的, 所以写 BFS还是稳妥一点

  • BFS 写法
const int N = 1000 + 10, M = 2 * N;
int n, m;
int k, q;
char g[N][N];

void bfs(int i ,int j) {
    queue<pair<int, int>> q;
    q.push({i, j});
    g[i][j] = '0';
    int dx[] = {-1, 0 ,1, 0}, dy[] = {0, 1, 0, -1};
    
    while (q.size()) {
        auto [x, y] = q.front(); q.pop();
        for (int c = 1; c <= k; c ++) {
            for (int i = 0; i < 4; i ++) {
                int a = x + c * dx[i], b = y + c * dy[i];
                if (a < 1 || a > n || b < 1 || b > m) continue;
                if (g[a][b] == '*') {
                    g[a][b] = '0';
                    q.push({a, b});
                }
            }
        }
    }
}

void solve() {
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++)   
        for (int j = 1; j <= m; j ++)
            cin >> g[i][j];
    
    int cnt = 0;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            if (g[i][j] == '*') {
                bfs(i, j);
                cnt ++;
            }
    
    cout << cnt;
}
  • DFS写法 (只能拿20分
const int N = 1000 + 10;
int n, m, k;
char g[N][N];
int dx[] = {-1, 0 ,1, 0}, dy[] = {0, 1, 0, -1};

void dfs(int x ,int y) {
    g[x][y] = '0';

    for (int c = 1; c <= k; c ++) {
        for (int i = 0; i < 4; i ++) {
            int a = x + c * dx[i], b = y + c * dy[i];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (g[a][b] == '*') dfs(a, b);
        }
    }
}

void solve() {
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++)   
        for (int j = 1; j <= m; j ++)
            cin >> g[i][j];
    
    int cnt = 0;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            if (g[i][j] == '*') {
                dfs(i, j);
                cnt ++;
            }
    
    cout << cnt;
}

7-10 会议安排

  • 贪心
  • 跟上次选拔赛那个贪心一模一样, 不多证明
  • 结论: 按结束时间从小到大排序, 然后从前到后依次安排即可
int n, m;

void solve() {
    cin >> n;
    vector<pair<int, int>> ac(n);
    for (auto& [en, be]: ac) cin >> be >> en;
    sort(ac.begin(), ac.end());
    int cnt = 1;
    int las = ac[0].first;
    for (int i = 1; i < ac.size(); i ++) {
        auto [en, be] = ac[i];
        if (be >= las) {
            las = en;
            cnt ++;
        }
    }
    cout << cnt << "\n";
}

你可能感兴趣的:(acm新生赛,算法,c++,数据结构)