AtCoder ABC184

本期题目依旧不是很难。
C题数学,D题DP,E题宽搜,F题是折半搜索模板题。

C - Super Ryuma

题意里有三种操作,我们记为o1,o2,o3
首先需要了解,如果奇偶性质相同,一定可以在两步内通过对角线移动到达另一个点。即 ( x 1 + y 1 ) % 2 = ( x 2 + y 2 ) % 2 (x1+y1) \% 2 =(x2+y2) \% 2 (x1+y1)%2=(x2+y2)%2
然后显然,最大需要3步到达,因为可以通过o3操作改变奇偶性。需要判断的是能否在2步内抵达。
0步和1步比较简单。考虑2步的情况。
前两种操作为对角线移动,一开始也许会考虑先进行两种对角线移动,然后进行o3操作,但是程序不太好写。实际上三种操作是可交换的,因此可以先进行第三种操作枚举,然后判断对角线能否到。
最后居然没有通过case after contest,原因是可以进行两次o3操作。

# -*- coding: utf-8 -*-
# @time     : 2023/6/2 13:30
# @file     : atcoder.py
# @software : PyCharm

import bisect
import copy
import sys
from itertools import permutations
from sortedcontainers import SortedList
from collections import defaultdict, Counter, deque
from functools import lru_cache, cmp_to_key
import heapq
import math
sys.setrecursionlimit(100010)


def m0(x0, y0, x1, y1):
    return x0 + y0 == x1 + y1


def m1(x0, y0, x1, y1):
    return x0 - y0 == x1 - y1


def m2(x0, y0, x1, y1):
    return abs(x0 - x1) + abs(y0 - y1) <= 3


def main():
    items = sys.version.split()
    fp = open("in.txt") if items[0] == "3.10.6" else sys.stdin
    x0, y0 = map(int, fp.readline().split())
    x1, y1 = map(int, fp.readline().split())
    if x0 == x1 and y0 == y1:
        print(0)
        return
    if m0(x0, y0, x1, y1) or m1(x0, y0, x1, y1) or m2(x0, y0, x1, y1):
        print(1)
        return
    if (x0 + y0) % 2 == (x1 + y1) % 2:
        print(2)
        return
    for x in range(x0 - 3, x0 + 4):
        for y in range(y0 - 3, y0 + 4):
            if m2(x, y, x0, y0):
                if m0(x, y, x1, y1) or m1(x, y, x1, y1) or m2(x, y, x1, y1):
                    print(2)
                    return
    print(3)


if __name__ == "__main__":
    main()

D - increment of coins

记忆化搜索还是非常好推的。
f ( i , j , k ) f(i,j,k) f(i,j,k)是三种coin有i,j,k个的情况下完成操作的次数期望
f ( i , j , k ) = f ( i + 1 , j , k ) ∗ i / s u m + f ( i , j + 1 , k ) ∗ j / s u m + f ( i , j , k + 1 ) / s u m + 1 f(i,j,k)=f(i+1,j,k)*i/sum+f(i,j+1,k)*j/sum+f(i,j,k+1)/sum+1 f(i,j,k)=f(i+1,j,k)i/sum+f(i,j+1,k)j/sum+f(i,j,k+1)/sum+1
边界是当任意一种coin达到100个

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <algorithm>
#define LT(x) (x * 2)
#define RT(x) (x * 2 + 1)


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef vector<int> vi;


double f[101][101][101];


double get(int a, int b, int c) {
    if (a == 100 || b == 100 || c == 100)
        return 0;
    if (f[a][b][c])
        return f[a][b][c];
    int s = a + b + c;
    double ret = double(a) / s * get(a + 1, b, c) + double(b) / s * get(a, b + 1, c) +
        double(c) / s * get(a, b, c + 1) + 1;
    f[a][b][c] = ret;
    return ret;
}


int main() { 
    //freopen("in.txt", "r", stdin);
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    double ans = get(a, b, c);
    printf("%.8f\n", ans);
    return 0;
}

E - Third Avenue

如果没有字母,那么就是一道简单的bfs题。
加入字母后,可以把相同的字母当成一个点。在传送的时候记录一下哪个字母已经发生传送,这样下一次遇到该字母时就不需要做传送操作。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <algorithm>
#define LT(x) (x * 2)
#define RT(x) (x * 2 + 1)


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef vector<int> vi;

int n, m;
char s[2003][2003];
map<char, vector<pii>> tel;
pii start, target;
queue<pii> qu;
bool vis[2003][2003];
int step[2003][2003];
bool alpha[256];


int main() { 
    //freopen("in.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++i)
    {
        scanf("%s", s[i]);
        for (int j = 0; j < m; ++j) {
            if (s[i][j] == 'S')
                start = { i, j };
            else if (s[i][j] == 'G')
                target = { i, j };
            else if (s[i][j] >= 'a' && s[i][j] <= 'z')
                tel[s[i][j]].push_back({ i, j });
        }
    }
    memset(step, -1, sizeof(step));
    int dir[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
    vis[start.first][start.second] = 1;
    step[start.first][start.second] = 0;
    qu.push(start);
    while (!qu.empty()) {
        pii cur = qu.front();
        qu.pop();
        int r = cur.first, c = cur.second;
        //printf("%d %d %d\n", r, c, step[r][c]);
        if (s[r][c] == 'G')
            break;
        if (s[r][c] >= 'a' && s[r][c] <= 'z') {
            if (!alpha[s[r][c]]) {
                for (pii& nxt : tel[s[r][c]]) {
                    int nr = nxt.first, nc = nxt.second;
                    if (vis[nr][nc] == 0) {
                        vis[nr][nc] = 1;
                        step[nr][nc] = step[r][c] + 1;
                        qu.push({ nr, nc });
                    }
                }
                alpha[s[r][c]] = 1;
            }
           
        }
        for (int i = 0; i < 4; ++i) {
            int nr = r + dir[i][0], nc = c + dir[i][1];
            if (nr >= 0 && nr < n && nc >= 0 && nc < m && s[nr][nc] != '#' && vis[nr][nc] == 0) {
                vis[nr][nc] = 1;
                step[nr][nc] = step[r][c] + 1;
                qu.push({ nr, nc });
            }
        }
    }
    printf("%d", step[target.first][target.second]);
    
    return 0;
}

F - Programming Contest

n = 20 n=20 n=20的话,暴力mask求解。
T < 1 0 6 T<10^6 T<106的话,简单的背包求解。
遗憾的是都不满足。
这个题的正统做法是折半搜索。思路是分两组mask求可能的和。
以其中一组遍历,二分求另一组可能的数。

另外有强行剪枝的方法,但是这里的空白太小了写不下。


#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <algorithm>
#define LT(x) (x * 2)
#define RT(x) (x * 2 + 1)


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef vector<int> vi;

int n;
LL T;
LL a[50];
set<LL> s0, s1;

int main() { 
    //freopen("in.txt", "r", stdin);
    scanf("%d%lld", &n, &T);
    for (int i = 0; i < n; ++i) {
        scanf("%lld", &a[i]);
    }
    vector<LL> v0(a, a + n / 2);
    vector<LL> v1(a + n / 2, a + n);
    int m = v0.size();
    for (int i = 0; i < (1 << m); ++i) {
        LL t = 0;
        for (int j = 0; j < m; ++j) {
            if ((i >> j) & 1) t += v0[j];
        }
        s0.insert(t);
    }
    m = v1.size();
    for (int i = 0; i < (1 << m); ++i) {
        LL t = 0;
        for (int j = 0; j < m; ++j) {
            if ((i >> j) & 1) t += v1[j];
        }
        s1.insert(t);
    }
    vector<LL> l0(s0.begin(), s0.end());
    vector<LL> l1(s1.begin(), s1.end());
    sort(l0.begin(), l0.end());
    sort(l1.begin(), l1.end());
    LL ans = 0;
    for (auto x : l0) {
        if (x == T)
        {
            ans = T;
            break;
        }
        if (x > T)
            break;
        int pos = lower_bound(l1.begin(), l1.end(), T - x) - l1.begin();

        if (pos - 1 >= 0) {
            ans = max(ans, x + l1[pos - 1]);
        }
        if (pos >= 0 && pos < l1.size() && x + l1[pos] <= T) {
            ans = max(ans, x + l1[pos]);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值