【动态规划】3 Atcoder Beginner Contest369 D题题解

1.题目描述

题目传送门 - Atcoder Beginner Contest369 D题题面

2.题意转化

给你 n n n 个数 a i   ( 1 ≤ i ≤ n ) a_i \ (1 \leq i \leq n) ai (1in) ,每个数可以选或不选。对于每一个选的数 a j a_j aj ,如果是从左往右第偶数个数,那么答案增加 a j ∗ 2   ( j m o d    2 = 0 ) a_j*2 \ (j \mod 2 = 0) aj2 (jmod2=0) ;否则答案增加 a j   ( j m o d    2 = 1 ) a_j \ (j \mod 2 = 1) aj (jmod2=1)

3.思路分析

对于每一个数 a i a_i ai ,都存在选或不选两种情况。这样我们很容易想到动态规划的0/1背包。

仔细一想,如果用0/1背包,需要用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示对于前 i i i 个数,选其中的 j j j 个。 k = 0 k = 0 k=0 表示不选第 i i i 个数, k = 1 k = 1 k=1 表示选第 i i i 个数。则有递推式: d p [ i ] [ j ] [ 0 ] = m a x ( d p [ i − 1 ] [ j ] [ 0 ] , d p [ i − 1 ] [ j ] [ 1 ] ) dp[i][j][0] = max(dp[i-1][j][0],dp[i-1][j][1]) dp[i][j][0]=max(dp[i1][j][0],dp[i1][j][1]) d p [ i ] [ j ] [ 1 ] = m a x ( d p [ i − 1 ] [ j − 1 ] [ 0 ] , d p [ i − 1 ] [ j − 1 ] [ 1 ] ) + a i + ( j m o d    2 ) ∗ a i dp[i][j][1] = max(dp[i-1][j-1][0],dp[i-1][j-1][1])+a_i+(j\mod2)*a_i dp[i][j][1]=max(dp[i1][j1][0],dp[i1][j1][1])+ai+(jmod2)ai

以上两个式子比较好理解,根据 j j j 的取值即可判断这是不是所选的第偶数个数字。

但是存在问题:

  1. 空间复杂度 O ( n 2 ) O(n^2) O(n2) ,除非在Atcoder否则不能通过此题。
  2. 时间复杂度,需要枚举 i , j , k i,j,k i,j,k ,时间复杂度 O ( n 2 ) O(n^2) O(n2) ,无法通过此题。

我们考虑dp的核心:不就是维护第 i i i 个数是所选的第几个数吗!

并且我们只关心所选的是不是第偶数个数,即设 b j b_j bj 是所选的第 j j j 个数,关键是维护 j m o d    2 = 0 j\mod2 = 0 jmod2=0 j m o d    2 = 1 j\mod2 = 1 jmod2=1 的两种情况。

那么,我们只需要用 j j j 来代替第二个维度,同时删去第三个维度即可。即定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个字符中,如果选 a i a_i ai 那么如果 j = 0 j = 0 j=0 ,说明它是第奇数个被选的;如果 j = 1 j = 1 j=1 ,说明它是第偶数个被选的。

那么,显然有以下递推式: d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + a [ i ] ∗ 2 ) dp[i][0] = max(dp[i-1][0],dp[i-1][1]+a[i]*2) dp[i][0]=max(dp[i1][0],dp[i1][1]+a[i]2) d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 0 ] + a [ i ] , d p [ i − 1 ] [ 1 ] ) dp[i][1] = max(dp[i-1][0]+a[i],dp[i-1][1]) dp[i][1]=max(dp[i1][0]+a[i],dp[i1][1])

需要注意的是,如果选 a i a_i ai ,那么 j j j 的取值需要变化(即0变1,1变0)。并且只有 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 时才可以考虑加上 a i ∗ 2 a_i*2 ai2

这样一来,时间复杂度为 O ( n ) O(n) O(n) ,可以通过此题。

4.注意事项

  • 需要开long long。
  • 注意区分dp的状态转移方程。
  • dp初始化: d p [ 1 ] [ 0 ] = 0 , d p [ 1 ] [ 1 ] = a [ 1 ] dp[1][0] = 0,dp[1][1] = a[1] dp[1][0]=0,dp[1][1]=a[1]
  • dp计算答案: m a x ( d p [ n ] [ 0 ] , d p [ n ] [ 1 ] ) max(dp[n][0],dp[n][1]) max(dp[n][0],dp[n][1])
  • 由于dp数组开了long long,所以需要定义一个最大值函数。

5.代码实现 & 题目练习

请根据题解讲解补全代码(第4题5分,第5题3分,其余一题2分,共14分)

  • 不允许在补全代码时参考以上题解,请独立完成,题解中有答案。
  • 本试题难度约为CSP-J第一轮代码补全第2题。
#include <bits/stdc++.h>
using namespace std;
#define intt long long
intt n,a[200005];
intt dp[200005][2];
intt maxx(intt a,intt b)
{
	if (__1__) return a;
	return b;
}
int main()
{
	cin >> n;
	for (int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	dp[1][0] = 0;
	dp[1][1] = a[1];
	for (int i=__2__;i<=n;i++)
	{
		dp[i][0] = maxx(dp[i-1][0],__3__);
		dp[i][1] = __4__
	}
	cout<<__5__;
	return 0;
}

  1. __1__处应填()
  • A. a < b
  • B. a > b
  • C.a == b
  • D.a == 0 || b == 0
  1. __2__处应填()
  • A.0
  • B.1
  • C.2
  • D.a[1]
  1. __3__处应填()
  • A. dp[i-1][0]+a[i]
  • B. dp[i-1][0]+a[i]*2
  • C. dp[i-1][1]+a[i]
  • D. dp[i-1][1]+a[i]*2
  1. __4__处应填()
  • A. maxx(dp[i-1][0]+a[i],dp[i-1][0])
  • B. maxx(dp[i-1][0]+a[i]*2,dp[i-1][1])
  • C. maxx(dp[i-1][1]+a[i],dp[i-1][0])
  • D. maxx(dp[i-1][1]+a[i]*2,dp[i-1][1])
  • E. maxx(dp[i-1][1],dp[i-1][1]+a[i])
  • F. maxx(dp[i-1][0]+a[i],dp[i-1][1])
  1. __5__处应填()
  • A. maxx(dp[n][0],dp[n][1])
  • B. maxx(dp[n-1][0],dp[n-1][1])
  • C. maxx(dp[1][0],dp[1][1])
  • D. dp[n][0]+dp[n][1]

6.本题小结

Atcoder Beginner Contest369 D题题解
主要用到的是dp优化的思路,当然也有别的优化方法,但是这种思路的时间复杂度显然是比较优的 O ( n ) O(n) O(n) ,也可以尝试 O ( n log ⁡ n ) O(n \log n) O(nlogn) 解决。整体难度【普及/提高-】左右,可能在CSP-J第2.3题或CSP-S第1题。

附:参考答案
1.B 2.C 3.D 4.F 5.A

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值