티스토리 뷰

[CF 1155D] Beautiful Array - 1900


문제 설명 : 

n개의 수로 이루어진 수열과 x가 주어진다. 연속된 수들의 합의 최댓값을 구하면 되는데, 최대 1번에 한하여 수열의 연속된 수들에 x를 곱하는 연산이 가능하다.


풀이 : 

처음에 풀 때는 그리디하게 접근하려 했는데 WA를 받았다(반례 4 -1 / -5 10 -3 10).

아무튼 dp로 풀어야 하는데, 이 문제의 핵심은 결국 몇 번째부터 몇 번째까지 x가 곱해지냐이다(물론 안 곱할 수도 있다). 그래서 나는 x가 곱해지는 모든 구간의 경우에 대해서 저장하고 그 중에서 최댓값을 구하자는 취지이다. 일반화시키자면, 정답은 (x를 안 곱하는 구간) + (x를 곱하는 구간) + (x를 안 곱하는 구간) 으로 구성될 것이다. 그리고 나는 곱해지냐 안 곱해지느냐의 경계 지점만 찾으면 된다는 것이다. 그래서 dp테이블은 총 3개가 필요하다. 방금 언급했다시피 첫 번째는 안 곱할 구간, 두 번째는 곱할 구간, 세 번째는 안 곱할 구간이다. 이 3개의 경우를 적절히 더해서 최대가 되는 경우를 찾으면 된다. 첫 번째는 선택을 고려할 필요 없이, prefix sum이 0이상이 되도록만 만들어 주면 된다. 두 번째와 세 번째가 선택의 여지가 생기는데,  (k-1)번째 에서 값을 가져오느냐 k번째 에서 가져오느냐의 문제이다. 두 번째를 기준으로 설명하면 1번째에서 가져온다는 말은 현재 수부터 x를 곱할 것이라는 의미이고, 2번째에서 가져온다는 말은 이전부터 계속 x를 곱해왔던 수에 누적해나가는 뜻이다. 그래서 둘 중에 큰 값을 취하면 된다. 3번째도 마찬가지로 2번째와 3번째의 값을 비교하면 된다. 


주의할 점 : 

정답(최댓값)이 무조건 n번째 수에서 나온다는 보장은 없다. 그래서 i번째를 돌 때마다 dp[0][i]~dp[2][i]가 모두 정답의 후보가 될 수 있음에 주의해야 한다.


배운 점 : 

아직 dp유형 문제는 다른 유형에 비해서 부족한 것 같다. 처음에 접근할 때는 그리디로 푸는데 WA를 받거나 반례를 찾았을 때는 dp로 우회해야겠다.


코드 : 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
#include<functional>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<assert.h>
#include<stdlib.h>
#include<stack>
using namespace std;
/*********************Contest Template***********************/
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<long long,long long> pll;
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(0);
struct S{
    int a,b; S(){}S(int _a,int _b)
    { a=_a; b=_b; }
    const bool operator<(const S &o) const{
        return a<o.a;}
};
priority_queue<int,vector<int>,greater<int>> mpq;
const int SPACE=0,NL=1;  string exm;
inline void showAll(vector<int> &v,int sep){ //0=space,1="\n"
    for(int &here:v) printf("%d%c",here,(sep)?'\n':' '); }
inline void exf(void){ cout<<exm<<"\n"; exit(0); }
inline vector<int> int_seperation(int N,int d=10){
    vector<int> v; while(N){v.push_back(N%d); N/=d;}
    reverse(v.begin(),v.end()); return v; }
/*********************Contest Template***********************/
const int SIZE= 300009;
 
ll dp[3][SIZE];
ll arr[SIZE];
 
int main(){
    int n,x;    scanf("%d %d",&n,&x);
 
    for(int i=1 ; i<=n ; i++){
        scanf("%lld",&arr[i]);
    }
 
    ll ans=0;
    for(int i=1 ; i<=n ; i++){
        dp[0][i]=max(0LL,dp[0][i-1]+arr[i]);
        dp[1][i]=max(dp[1][i-1],dp[0][i-1])+arr[i]*x;
        dp[2][i]=max(dp[2][i-1],dp[1][i-1])+arr[i];
 
        for(int j=0 ; j<3 ; j++){
            ans=max(ans,dp[j][i]);
        }
    }
 
    printf("%lld\n",ans);
    
    return 0;
}
 
 
cs


'Problem Solving > Dynamic Programming' 카테고리의 다른 글

[CF 1092F] Tree with Maximum Cost  (0) 2019.11.14
[CF 1082E] Increasing Frequency  (0) 2019.11.14
[CF 1114D] Flood Fill  (0) 2019.07.25
[Codeforces 1036C] Classy Numbers  (0) 2019.05.13
[Codefocres 1061C] Multiplicity  (0) 2019.04.29
댓글