2023杭电暑假多校9 题解 2 5 8 | JorbanS

文章目录

  • [2-Shortest path](https://vjudge.csgrandeur.cn/problem/HDU-7372)
  • [5-List Reshape](https://vjudge.csgrandeur.cn/problem/HDU-7375)
  • 8-Coins

2-Shortest path

题意 1 1 1 经过最少次的操作变成 n n n,对于一个数 x x x 有以下三种操作

  1. x = 2 x x=2x x=2x
  2. x = 3 x x=3x x=3x
  3. x = x + 1 x=x+1 x=x+1

题解 反过来看,从 n n n 变换到 1 1 1,先进行 1 ∼ 1 0 6 1\sim 10^6 1106 区间内的预处理,随后使用 H a s h   m a p Hash~map Hash map 记忆化搜素

因为进行两次操作 x − − x-- x 和一次 x / = 2 x/=2 x/=2 是亏的,等价于 x / = 2 x/=2 x/=2 x − − x-- x;同理也不能连续进行三次 x − − x-- x 再进行一次 x / = 3 x/=3 x/=3

const int N = 1e6 + 2;
unordered_map<ll, int> mp;
int f[N];

void init() { // 预处理
    memset(f, 0x3f, sizeof f);
    f[1] = 0;
    for (int i = 1; i < N; i ++) {
        if (i * 2 < N) f[i * 2] = min(f[i * 2], f[i] + 1);
        if (i * 3 < N) f[i * 3] = min(f[i * 3], f[i] + 1);
        if (i + 1 < N) f[i + 1] = min(f[i + 1], f[i] + 1);
    }
}

ll dfs(ll n) {
    if (n < N) return f[n];
    if (mp.count(n)) return mp[n]; // 记忆化
    return mp[n] = min(
        dfs(n / 2) + n % 2 + 1,
        dfs(n / 3) + n % 3 + 1
    );
}

int main() {
    init();
    FastIO
    Cases
    {
        ll n; cin >> n;
        cout << dfs(n) << endl;
    }
    return 0;
}

其实不用预处理也行,但是要特判 n < 2 n<2 n<2 r e t u r n   0 return~0 return 0

  • n = 1 n=1 n=1 时,答案确实正确
  • n = 0 n=0 n=0 时,实际上是当 n = 2 n=2 n=2 递归下去会出现此情况,如果 r e t u r n   0 return~0 return 0 m i n min min 里面的两种 d f s dfs dfs 结果相同,实际上 d f s ( n / 3 ) dfs(n/3) dfs(n/3) 是不合法的
unordered_map<ll, int> mp;

ll dfs(ll n) {
    if (n < 2) return 0;
    if (mp.count(n)) return mp[n]; // 记忆化
    return mp[n] = min(
        dfs(n / 2) + n % 2 + 1,
        dfs(n / 3) + n % 3 + 1
    );
}

int main() {
    init();
    FastIO
    Cases
    {
        ll n; cin >> n;
        cout << dfs(n) << endl;
    }
    return 0;
}

5-List Reshape

题意 将数组 [ a 1 , a 2 , a 3 , . . . , a n ] [a_1,a_2,a_3,...,a_n] [a1,a2,a3,...,an] 分割成 n n n 维数组每组大小为 l l l,即 [ [ a 1 , . . . , a l ] , [ a l + 1 , . . . , a 2 l ] , . . . ] [[a_1,...,a_l],[a_{l+1},...,{a_{2l}}],...] [[a1,...,al],[al+1,...,a2l],...]

void solve() {
    string s, str;
    cin.ignore();
    getline(cin, s);
    int n, l; cin >> n >> l;
    int cnt = 0, num = 0;
    for (int i = 0; i < s.size(); i ++) {
        char c = s[i];
        if (c == ',' || c == ']') cnt ++;
        if (cnt == l) {
            cout << "[" << str << ']';
            num ++, i ++, cnt = 0, str = "";
            if (num != n) cout << ", ";
        }
        else str += c;
    }
    cout << ']' << endl;
}

8-Coins

题意 n n n 个人,每个人 a i a_i ai 个硬币,每次选两人其中一人给另一个人一个硬币,如果某人没有硬币则退出游戏,问最后进行轮数的期望值(保证为整数)

题解 打表猜结论‍♂️‍♂️‍♂️

对于每一轮两个人,选择哪个人给硬币的概率为 50 % 50\% 50%,则游戏轮数的期望为 f ( m , n ) = 1 + 50 % ⋅ f ( m + 1 , n − 1 ) + 50 % ⋅ f ( m − 1 , m + 1 ) f(m,n)=1+50\%·f(m+1,n-1)+50\%·f(m-1,m+1) f(m,n)=1+50%f(m+1,n1)+50%f(m1,m+1)

// 打表 code
double dfs(ll m, ll n, ll dep) {
    if (dep == 40 || !m || !n) return 0;
    return 1 + dfs(m + 1, n - 1, dep + 1) / 2 + dfs(m - 1, n + 1, dep + 1) / 2;
}
cout << dfs(m, n, 0) << endl;
1 2 3 4 5 6
1 1 2 3 3.99921 4.9852 5.91537
2 4 5.99872 7.97463 9.8475
3 8.9704 11.8098

根据打表结果猜测 f ( n , m ) = n ⋅ m f(n,m)=n·m f(n,m)=nm

对于三个人,由样例 in:1 1 1, out:3 猜测是 a 1 a 2 + a 2 a 3 + a 3 a 1 a_1a_2+a_2a_3+a_3a_1 a1a2+a2a3+a3a1

再推广到 n n n 个人,两两相乘然后相加即可,从 O ( n 2 ) O(n^2) O(n2) 优化到 O ( n ) O(n) O(n),答案即为 ∑ 1 ≤ i ≤ j ≤ n a i a j = ∑ i = 1 n a i ⋅ ( s u m − a i ) \sum_{1\le i\le j\le n}a_ia_j=\sum_{i=1}^na_i·(sum-a_i) 1ijnaiaj=i=1nai(sumai)

using lll = __int128;

void write(lll x) {
    if (x > 9) write(x / 10);
    cout << char(x % 10 + '0');
}

void solve() {
    int n; cin >> n;
    vector<int> a(n);
    lll res = 0, sum = 0;
    for (auto &x: a) cin >> x, sum += x;
    for (auto i : a) res += (sum - i) * i;
    write(res / 2); cout << endl;
}

你可能感兴趣的:(OI,题解,c++,算法)