ACM_day6

 最大子段和

 洛谷 - P1115 

Description

给出一个长度为 nn 的序列 aa,选出其中连续且非空的一段使得这段和最大。

Input

第一行是一个整数,表示序列的长度 nn。

第二行有 nn 个整数,第 ii 个整数表示序列的第 ii 个数字 aiai​。

Output

输出一行一个整数表示答案。

Sample 1

InputcopyOutputcopy
7
2 -4 3 -1 2 -4 3
4

Hint

样例 1 解释

选取 [3,5][3,5] 子段 {3,−1,2}{3,−1,2},其和为 44。

数据规模与约定
  • 对于 40%40% 的数据,保证 n≤2×103n≤2×103。
  • 对于 100%100% 的数据,保证 1≤n≤2×1051≤n≤2×105,−104≤ai≤104−104≤ai​≤104。

解题思路

我们可以通过遍历数组来找到和最大的连续子序列。具体方法是,在遍历时不断累加当前元素的值,如果累加后的结果小于当前元素的值,说明之前的累加和为负数,对最大和没有贡献,因此我们可以舍弃之前的累加和,从当前元素重新开始累加。最终,遍历完成后得到的最大累加和即为所求的最大子序列和。

ACcode 

#include <iostream>
 
using namespace std;
 
typedef long long ll;
 
int main()
{
    int n;
    cin >> n;
    ll ans = -1e9;
    ll a = 0, b = 0;
    for(int i=0; i<n; i++)
    {
        cin >> a;
        if(i == 0)
        {
            b = a;
        }
        else
        {
            b = max(b+a, a);
        }
        ans = max(ans, b);
    }
    cout << ans;
    return 0;
}

 采药

 洛谷 - P1048 

Description

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

Input

第一行有 22 个整数 TT(1≤T≤10001≤T≤1000)和 MM(1≤M≤1001≤M≤100),用一个空格隔开,TT 代表总共能够用来采药的时间,MM 代表山洞里的草药的数目。

接下来的 MM 行每行包括两个在 11 到 100100 之间(包括 11 和 100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

Output

输出在规定的时间内可以采到的草药的最大总价值。

Sample 1

InputcopyOutputcopy
70 3
71 100
69 1
1 2
3

Hint

【数据范围】

  • 对于 30%30% 的数据,M≤10M≤10;
  • 对于全部的数据,M≤100M≤100。

【题目来源】

NOIP 2005 普及组第三题

解题思路本题与背包问题类似,核心在于在有限的时间内获取最大价值的物品。我们可以采用动态规划的方法来解决,既可以使用记忆化搜索,也可以使用递推的方式。递推的思路是:定义状态dp[i][j]表示考虑前i件物品,在总时间不超过j的情况下所能获得的最大价值。状态转移方程为:dp[i][j] = max(dp[i-1][j], dp[i-1][j-t[i]] + w[i]),其中dp[i-1][j]表示不选择第i件物品,dp[i-1][j-t[i]] + w[i]表示选择第i件物品。通过这个状态转移方程,我们可以逐步计算出最终的最大价值。

ACcode

记忆化搜索

#include <iostream>
 
using namespace std;
 
int w[110], tt[110];
int dp[1010][110];
int T, M;
 
int dfs(int t, int m)
{
    if(m == M)
    return 0;
    if(dp[t][m] > 0)
    return dp[t][m];
    if(t+tt[m] <= T)
    return dp[t][m] = max(dfs(t+tt[m], m+1)+w[m], dfs(t, m+1));
    else
    return dp[t][m] = dfs(t, m+1);
}
 
int main()
{
    cin >> T >> M;
    for(int i=0; i<M; i++)
    {
        cin >> tt[i] >> w[i];
    }
    cout << dfs(0, 0);
    return 0;
}

 宝物筛选

 洛谷 - P1776 

Description

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 WW 的采集车,洞穴里总共有 nn 种宝物,每种宝物的价值为 vivi​,重量为 wiwi​,每种宝物有 mimi​ 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

Input

第一行为一个整数 nn 和 WW,分别表示宝物种数和采集车的最大载重。

接下来 nn 行每行三个整数 vi,wi,mivi​,wi​,mi​。

Output

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

Sample 1

InputcopyOutputcopy
4 20
3 9 3
5 9 1
9 4 2
8 1 3
47

Hint

对于 30%30% 的数据,n≤∑mi≤104n≤∑mi​≤104,0≤W≤1030≤W≤103。

对于 100%100% 的数据,n≤∑mi≤105n≤∑mi​≤105,0≤W≤4×1040≤W≤4×104,1≤n≤1001≤n≤100。

 

解题思路

本题本质上是多重背包问题的一种变体,其核心思路与上一题类似,区别在于每种物品可能有多个。为了将其转化为01背包问题,我们可以将每种物品拆分成多个独立的物品,例如,如果物品1有n个,我们可以将其视为n个相同的独立物品,每个物品只能选择一次。然而,由于数据范围较大,直接拆分会导致效率低下,因此我们需要采用二进制优化方法。

二进制优化的思想是将每种物品的数量按照二进制表示进行拆分,例如,将n拆分为1, 2, 4, ..., 2^k, n-(2^(k+1)-1),这样可以将物品数量从n减少到log(n)级别,从而显著降低时间复杂度。通过这种方式,我们能够高效地解决多重背包问题,同时避免因数据范围过大而导致的性能问题。

ACcode

#include <iostream>
 
using namespace std;
 
const int N = 1e6 + 10;
int n, W, cnt;
int v[N], w[N];
int dp[N];
 
int main()
{
    cin >> n >> W;
    for(int i=1; i<=n; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        for(int j=1; j<=z; j<<=1)
        {
            v[++cnt] = x * j;
            w[cnt] = y * j;
            z -= j;
        }
        if(z)
        {
            v[++cnt] = x * z;
            w[cnt] = y * z;
        }
    }
    for(int i=1; i<=cnt; i++)
    {
        for(int j=W; j>=w[i]; j--)
        {   
            dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
        }
    }
    cout << dp[W];
    return 0;
}

 最长公共子序列

 洛谷 - P1439 

Description

给出 1,2,…,n1,2,…,n 的两个排列 P1P1​ 和 P2P2​ ,求它们的最长公共子序列。

Input

第一行是一个数 nn。

接下来两行,每行为 nn 个数,为自然数 1,2,…,n1,2,…,n 的一个排列。

Output

一个数,即最长公共子序列的长度。

Sample 1

InputcopyOutputcopy
5 
3 2 1 4 5
1 2 3 4 5
3

Hint

  • 对于 50%50% 的数据, n≤103n≤103;
  • 对于 100%100% 的数据, n≤105n≤105。

 

#include <iostream>
 
using namespace std;
 
const int N = 1e5 + 10;
 
int n, len;
int a[N], b[N];
int f[N];
 
int bound(int x)
{
    int l=1, r=len;
    while(l<r)
    {
        int mid = (l+r)/2;
        if(b[f[mid]]>b[x])
        r = mid;
        else
        l = mid + 1;
    }
    return r;
}
 
int main()
{
    cin >> n;
    for(int i=1; i<=n; i++)
    {
        cin >> a[i];
    }
    for(int i=1; i<=n; i++)
    {
        int d;
        cin >> d;
        b[d] = i;
    }
    for(int i=1; i<=n; i++)
    {
        if(b[a[i]] > b[f[len]])
        f[++len] = a[i];
        else
        f[bound(a[i])] = a[i];
    }
    cout << len;
    return 0;
}

 Kevin and Puzzle

 CodeForces - 2061C 

Kevin enjoys logic puzzles.

He played a game with nn classmates who stand in a line. The ii-th person from the left says that there are aiai​ liars to their left (not including themselves).

Each classmate is either honest or a liar, with the restriction that no two liars can stand next to each other. Honest classmates always say the truth. Liars can say either the truth or lies, meaning their statements are considered unreliable.

Kevin wants to determine the number of distinct possible game configurations modulo 998244353998244353. Two configurations are considered different if at least one classmate is honest in one configuration and a liar in the other.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤1041≤t≤104). The description of the test cases follows.

The first line of each test case contains an integer nn (1≤n≤2⋅1051≤n≤2⋅105) — the number of classmates.

The second line contains nn integers a1,a2,…,ana1​,a2​,…,an​ (0≤ai≤n0≤ai​≤n) — the number of liars to the left of the ii-th person they claimed.

It is guaranteed that the sum of nn over all test cases does not exceed 2⋅1052⋅105.

Output

For each test case, output one integer — the number of distinct game configurations modulo 998244353998244353.

Examples

InputcopyOutputcopy
8
3
0 1 2
5
0 0 0 0 0
5
0 0 1 1 2
5
0 1 2 3 4
5
0 0 1 1 1
5
5 1 5 2 5
1
0
4
2 3 1 1
1
2
3
0
4
1
2
0

Note

We will use redred to mark liars and blueblue to mark honest people.

In the first test case, the only possible way is (0,1,2)(0,1,2).

In the second test case, two possible ways are (0,0,0,0,0)(0,0,0,0,0) and (0,0,0,0,0)(0,0,0,0,0).

In the third test case, three possible ways are (0,0,1,1,2)(0,0,1,1,2), (0,0,1,1,2)(0,0,1,1,2), (0,0,1,1,2)(0,0,1,1,2).

解题思路

本题要求我们计算所有可能的方案数。我们可以通过动态规划来解决:定义状态dp[i][0]和dp[i][1],分别表示第i个人是骗子和不是骗子的方案数。状态转移方程为:

  • 如果第i个人是骗子,那么前一个人必须不是骗子,因此dp[i][0] = dp[i-1][1]。

  • 如果第i个人不是骗子,那么前一个人可以是骗子或不是骗子,因此dp[i][1] = dp[i-1][0] + dp[i-1][1]。

通过这种递推关系,我们可以逐步计算出第n个人的所有可能方案数,最终结果为dp[n][0] + dp[n][1]。这种方法清晰地表达了问题的逻辑,并能够高效地求解。

ACcode

#include <iostream>
 
using namespace std;
 
const int MOD = 998244353;
const int N = 2e5 + 10;
int a[N];
int dp[N][2];
 
int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        cin >> n;
        for(int i=1; i<=n; i++)
        {
            cin >> a[i];
            dp[i][0] = dp[i][1] = 0;
        }
        if(a[1]==0)
        dp[1][1] = 1;
        else
        dp[1][1] = 0;
        dp[1][0] = 1;
        for(int i=2; i<=n; i++)
        {
            dp[i][0] = dp[i-1][1];
            if(a[i] == a[i-1])
            dp[i][1] += dp[i-1][1];
            if(a[i] == a[i-2]+1)
            dp[i][1] += dp[i-1][0];
            dp[i][1] %= MOD;
        }
        cout << (dp[n][0] + dp[n][1]) % MOD << '\n';
    }
    return 0;
}

 World is Mine

 CodeForces - 1987D 

Alice and Bob are playing a game. Initially, there are nn cakes, with the ii-th cake having a tastiness value of aiai​.

Alice and Bob take turns eating them, with Alice starting first:

  • In her turn, Alice chooses and eats any remaining cake whose tastiness is strictly greater than the maximum tastiness of any of the cakes she's eaten before that. Note that on the first turn, she can choose any cake.
  • In his turn, Bob chooses any remaining cake and eats it.

The game ends when the current player can't eat a suitable cake. Let xx be the number of cakes that Alice ate. Then, Alice wants to maximize xx, while Bob wants to minimize xx.

Find out how many cakes Alice will eat if both players play optimally.

Input

Each test contains multiple test cases. The first line of input contains a single integer tt (1≤t≤5001≤t≤500) — the number of test cases. The description of the test cases follows.

The first line of each test case contains a single integer nn (1≤n≤50001≤n≤5000) — the number of cakes.

The second line of each test case contains nn integers a1,a2,…,ana1​,a2​,…,an​ (1≤ai≤n1≤ai​≤n) — the tastiness values of the cakes.

It is guaranteed that the sum of nn over all test cases does not exceed 50005000.

Output

For each test case, output a single integer — the number of cakes Alice will eat if both players play optimally.

Examples

InputcopyOutputcopy
9
4
1 4 2 3
3
1 1 1
5
1 4 2 3 4
4
3 4 1 4
1
1
8
4 3 2 5 6 8 3 4
7
6 1 1 3 5 3 1
11
6 11 6 8 7 5 3 11 2 3 5
17
2 6 5 3 9 1 6 2 5 6 3 2 3 9 6 1 6
2
1
3
2
1
3
2
4
4

Note

In the first test case, one possible sequence of turns is:

  1. Alice eats a cake with a tastiness value of 11. The remaining cakes are [4,2,3][4,2,3].
  2. Bob eats a cake with a tastiness value of 22. The remaining cakes are [4,3][4,3].
  3. Alice eats a cake with a tastiness of 33. The remaining cakes are [4][4].
  4. Bob eats a cake with a tastiness value of 44. The remaining cakes are [][].
  5. Since there are no more cakes left, the game ends.

In the second test case, one possible sequence of turns is:

  1. Alice eats a cake with a tastiness value of 11. The remaining cakes are [1,1][1,1].
  2. Bob eats a cake with a tastiness value of 11. The remaining cakes are [1][1].
  3. Since Alice has already eaten a cake with a tastiness value of 11, she cannot make a turn, so the game ends.

解题思路

我们发现,Alice 的最优策略是每次选择当前能取的美味值最小的蛋糕,而 Bob 的策略无法通过贪心算法直接维护。因此,我们采用动态规划来解决 Bob 的策略问题。

观察到,Bob 若想让 Alice 无法吃到某种蛋糕,必须将该种类的所有蛋糕全部拿走。为此,我们可以使用一个桶(数组)来记录每种蛋糕的出现次数。

将问题转化为:Bob 拿蛋糕的代价为该种类蛋糕的数量加 1(加 1 是因为 Alice 是先手),而 Bob 的收益为 1(即成功阻止 Alice 吃到该种类蛋糕)。于是,我们可以定义状态 dp[i][j],表示 Bob 在前 i 个蛋糕中,花费代价不超过 j 时,能够拿掉的最大蛋糕种类数。

状态转移方程为:
- 如果 Bob 不拿第 i 种蛋糕,则 dp[i][j] = dp[i-1][j]。
- 如果 Bob 拿第 i 种蛋糕,则 dp[i][j] = max(dp[i][j], dp[i-1][j-cost[i]] + 1),其中 cost[i] 为第 i 种蛋糕的代价。

最终答案为总蛋糕种类数减去 Bob 能够拿掉的最大蛋糕种类数,即 `种类总数 - dp[m][n]`,其中 m 为蛋糕种类数,n 为 Bob 的总代价。

该方法的时间复杂度为 O(n^2),能够高效解决问题。

ACcode 

#include <iostream>
#include <algorithm>
 
using namespace std;
 
const int N = 5010;
int n, cnt, a[N], c[N];
int f[N][N];
 
int solve()
{
    cin >> n;
    for(int i=1; i<=n; i++)
    {
        cin >> a[i];
        c[i] = 0;
    }
    sort(a+1, a+1+n);
    cnt = 1;
    c[1] = 1;
    for(int i=2; i<=n; i++)
    {
        if(a[i] == a[i-1])
        c[cnt]++;
        else
        c[++cnt]++;
    }
    for(int i=1; i<=cnt; i++)
    {
        for(int j=0; j<=i; j++)
        {
            if(j)
            f[i][j] = f[i-1][j-1] + 1;
            else
            f[i][j] = f[i-1][j] + 1;
            if(j+c[i]<i)
            f[i][j] = min(f[i][j], f[i-1][j+c[i]]);
        }
    }
    return f[cnt][0];
}
 
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cout << solve() << '\n';
    }
    return 0;
}

学习体会

通过对《动态规划初步》这份资料的深入学习,我对动态规划这一算法有了更为深刻的理解。动态规划通过将问题划分为多个阶段,并定义状态与决策,能够高效解决各类最优化问题。其核心思想在于通过“记忆化”避免重复计算,从而显著提升效率。

在学习过程中,我深刻感受到动态规划思想的精妙之处。资料中的引例和理论基础帮助我构建了全面的认知框架。通过不断的思考与实践,我逐渐掌握了状态定义、转移方程设计以及边界条件处理等关键技巧。未来,我将继续巩固这一算法,并在实际编程中灵活运用,以解决更复杂的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值