牛客刷题-Day10

牛客刷题-Day10

今日刷题:\(1046-1050\)

1046 [USACO 2016 Ope P]262144

题目描述

Bessie likes downloading games to play on her cell phone, even though she does find the small touch screen rather cumbersome to use with her large hooves.
She is particularly intrigued by the current game she is playing. The game starts with a sequence of \(N\) positive integers (\(2≤N≤262,144\)), each in the range \(1…40\). In one move, Bessie can take two adjacent numbers with equal values and replace them a single number of value one greater (e.g., she might replace two adjacent \(7\)s with an \(8\)). The goal is to maximize the value of the largest number present in the sequence at the end of the game. Please help Bessie score as highly as possible!

输入描述

The first line of input contains \(N\), and the next \(N\) lines give the sequence of \(N\) numbers at the start of the game.

输出描述

Please output the largest integer Bessie can generate.

示例

输入

4
1
1
1
2

输出

3

说明
In this example shown here, Bessie first merges the second and third 1s to obtain the sequence \(1\) \(2\) \(2\), and then she merges the \(2\)s into a \(3\). Note that it is not optimal to join the first two \(1\)s.

解题思路

  • 状态表示:\(f_{i,j}\) 表示从左端点 \(i\) 开始能合成 \(j\) 的右端点,也就是区间 \([i,f_{i,j}]\) 可以合成 \(j\)
  • 状态计算:如果想要合并成为 \(j+1\),则需要 \([i,f_{i,j}]\)\([f_{i,j}+1, f_{f_{i,j}+1,j}]\) 存在,转移方程为 \(f_{i,j+1}=f_{f_{i,j}+1,j}\)

这里的运用的是倍增的思想,由于 \(2^{18}=262144\),因此合成数最大为 \(58\)

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 300010, M = 60;

int n, a[N];
int f[N][M]; // f[i][j] 表示从左端点 i 开始能合成 j 的右端点

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        f[i][a[i]] = i;
    }
    int ans = 0;
    for (int j = 1; j <= 58; j++)
        for (int i = 1; i <= n; i++) {
            if (f[i][j] && f[f[i][j] + 1][j]) {
                f[i][j + 1] = f[f[i][j] + 1][j];
                ans = max(ans, j + 1);
            }
        }
    printf("%d\n", ans);
    return 0;
}

1048 [NOIP2007]守望者的逃离

题目描述

恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变。守望者在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去。到那时,岛上的所有人都会遇难。守望者的跑步速度为 \(17m/s\),以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在 \(1s\) 内移动 \(60m\),不过每次使用闪烁法术都会消耗魔法值 \(10\) 点。守望者的魔法值恢复的速度为 \(4\)\(/s\),只有处在原地休息状态时才能恢复。
现在已知守望者的魔法初值 \(M\),他所在的初始位置与岛的出口之间的距离 \(S\),岛沉没的时间 \(T\)。你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(\(s\))为单位,且每次活动的持续时间为整数秒。距离的单位为米(\(m\))。

输入描述

包括空格隔开的三个非负整数 \(M,S,T\)

输出描述

\(1\) 行为字符串 YesNo(区分大小写),即守望者是否能逃离荒岛。
\(2\) 行包含一个整数。第一行为 Yes(区分大小写)时表示守望者逃离荒岛的最短时间;第一行为 No(区分大小写)时表示守望者能走的最远距离。

示例1

输入

39 200 4

输出

No
197
示例2

输入

36 255 10

输出

Yes
6

备注

\(30\%\) 的数据满足:\(1 ≤ T ≤ 10, 1 ≤ S ≤ 100\)
\(50\%\) 的数据满足:\(1 ≤ T ≤ 1000, 1 ≤ S ≤ 10000\)
\(100\%\) 的数据满足:\(1 ≤ T ≤ 300000, 0 ≤ M ≤ 1000, 1 ≤ S ≤ 10^8\)

解题思路

  • 状态表示:\(f_i\) 表示第 \(i\) 秒走到的位置,需要求解最大值;
  • 状态计算:首先考虑只使用闪烁可以到达的位置,然后再考虑是否可以使用正常移动到达更远。

只要某一时刻不小于 \(S\),则可以逃出;否则,不可以。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 300010;

int M, S, T;
int f[N]; // 表示在第 i 秒走到的位置
int SPEED = 17, FLASH = 60, COST = 10, RECOVER = 4;

int main() {
    cin >> M >> S >> T;
    for (int i = 1; i <= T; i++) {
        if (M >= 10) {
            f[i] = f[i - 1] + FLASH;
            M -= COST;
        } else {
            f[i] = f[i - 1];
            M += RECOVER;
        }
    }
    for (int i = 1; i <= T; i++) {
        f[i] = max(f[i], f[i - 1] + SPEED);
        if (f[i] >= S) {
            puts("Yes");
            cout << i << endl;
            return 0;
        }
    }
    puts("No");
    cout << f[T] << endl;
    return 0;
}

1049 [NOIP2018]货币系统

题目描述

在网友的国度中共有 \(n\) 种不同面额的货币,第 \(i\) 种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 \(n\)、面额数组为 \(a[1..n]\) 的货币系统记作 \((n,a)\)
在一个完善的货币系统中,每一个非负整数的金额 \(x\) 都应该可以被表示出,即对每一个非负整数 \(x\),都存在 \(n\) 个非负整数 \(t[i]\) 满足 \(a[i]\times t[i]\) 的和为 \(x\)。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 \(x\) 不能被该货币系统表示出。例如在货币系统 \(n=3,a=[2,5,9]\) 中,金额 \(1,3\) 就无法被表示出来。
两个货币系统 \((n,a)\)\((m,b)\) 是等价的,当且仅当对于任意非负整数 \(x\),它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 \((m,b)\),满足 \((m,b)\) 与原来的货币系统 \((n,a)\) 等价,且 \(m\) 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 \(m\)

输入描述

输入的第一行包含一个整数 \(T\),表示数据组数。接下来按照如下格式分别给出 \(T\) 组数据。
每组数据的第一行包含一个正整数 \(n\)。接下来一行包含 \(n\) 个由空格隔开的正整数 \(a[i]\)

输出描述

输出文件共 \(T\) 行, 对于每组数据,输出一行一个正整数, 表示所有与 \((n,a)\) 等价的货币系统 \((m,b)\) 中,最小的 \(m\)

示例1

输入

2
4
3 19 10 6
5
11 29 13 19 17

输出

2
5

说明:在第一组数据中,货币系统 \((2, [3,10])\) 和给出的货币系统 \((n,a)\) 等价,并可以验证不存在 \(m < 2\) 的等价的货币系统,因此答案为 \(2\)
在第二组数据中,可以验证不存在 \(m < n\) 的等价的货币系统,因此答案为 \(5\)

备注

\(1 <= T <= 20, 1 <= n <= 100, 1 <= a[i] <= 25000\)

解题思路

等价的定义是:两个货币系统能凑出的所有非负整数金额的集合完全相同
首先对于 \((n,A)\) 中的某一元素 \(a\in A\),如果存在更小的元素 \(x,y...\),使得 \(a\) 可以被这些元素表示出来,那么这个 \(a\) 就是一个冗余元素,可以从 \(A\) 中去除。
当去除完成,可以得到一个集合 \(B\),这个 \((m,B)\)\((n,A)\) 是等价的。

对于一个最小集合,\(B\) 中的元素一定都在 \(A\) 中。
证明:如果存在 \(b\in B\)\(b\notin A\),因为等价,则存在 \(x_i\) 使得 \(\sum x_i\times a_i=b\)。考虑 \(B'=(B-\{b\})\cup \{a_i\}\),这样新集合元素个数一定是多于原集合的。那么既然 \(b\) 可以由 \(A\) 表示,而 \(A\) 中的元素又都可以由 \(B\) 表示(因为等价),那么 \(b\) 本身其实也可以由 \(B\) 中除了 \(b\) 以外的其它元素组合出来。
那么 \(b\) 是冗余的,可以去除,则去除之后的集合必然为原集合子集,这与 \(B\) 是最小的矛盾。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 25010;

int T, n, a[N], f[M];

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        int MAX = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            MAX = max(MAX, a[i]);
        }
        memset(f, 0, sizeof f);
        f[0] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = a[i]; j <= MAX; j++)
                f[j] += f[j - a[i]];
        int cnt = 0;
        for (int i = 1; i <= n; i++)
            if (f[a[i]] == 1)
                cnt++;
        printf("%d\n", cnt);
    }
    return 0;
}
posted @ 2025-10-09 11:15  Cocoicobird  阅读(6)  评论(0)    收藏  举报