https://www.acmicpc.net/problem/1912
문제의 조건을 보면 n의 크기가 100000이다.
시간 복잡도를 계산해보면 O(N^2)가 될 경우 시간초과가 나기 때문에 dp로 접근해야 한다.
문제를 보면서 2가지 방법을 생각했다.
첫 번째는 흔히 우리가 알고 있는 dp점화식을 세운 뒤에 푸는 방법,
두 번째는 수학적으로 계산하는 방식이다.
첫 번째 방법은 dp[n]을 n까지를 고려 했을 때 연속합이다.
즉 dp[n]은 n번째를 포함할 때는 dp[n-1]과 n번째의 값을 더한 값이 될 것이고, n번째까지 연속해서 더하지 않을 경우에
dp[n]은 n번째의 값인 arr[i]이다. 그리고 반복문을 돌 때마다 출력해야할 ans값의 최대값을 계산해 주면 된다.
두 번째 방법은 첫 번째 방법과 유사하지만 sum이라는 변수를 이용했다.
문제를 접근할 때 n번째를 더해줄지 말지를 계산하는 식으로 했다. n번째의 dp값을 계산하는 것이 아닌,
sum이라는 변수를 통해서 n번째까지 더해줄 경우에 sum의 값에 n번째 값을 더해주고
만약에 포함하지 않을 경우에는 n번째부터 시작하는 것으로 계산하였다.
이 경우에 만약 n번째를 포함하여 연속합을 구한 경우에도 0보다 작고 n번째 값 자체도 0보다 작을 경우에는
sum의 값을 0으로 설정하여 n+1번째부터 계산할 수 있도록 하였다.
만약 이대로 제출 한다면 하나의 반례때문에 오류가 날것이다. 바로 모든 값이 음수인 경우이다.
sum의 값을 0으로 하게 되면 최대값이 0 이 나올텐데, 실제로는 음수 값 중에서 가장 큰 값을 출력해야 한다.
그렇기 때문에 맨 처음에 양수인 값의 개수를 구한 다음에 모두 음수인 경우, 음수 중에서 최대값을 구해서 그 값을 출력하면 된다.
#1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <iostream> #include <algorithm> using namespace std; int main(){ int n,ans=0; cin>>n; int arr[100001]={0}; int dp[100001]={0}; for(int i=1;i<=n;i++) scanf("%d",&arr[i]); ans=dp[1]=arr[1]; for(int i=2;i<=n;i++){ dp[i]=max(arr[i],dp[i-1]+arr[i]); ans=max(ans,dp[i]); } cout<<ans<<endl; return 0; } | cs |
#2
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 | #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int n,ans=-1000,sum; cin>>n; int cnt=0; //양수 개수 vector<int> vec(n,0); for(int i=0;i<n;i++){ scanf("%d",&vec[i]); if(vec[i]>=0) cnt++; ans=max(ans,vec[i]); } if(cnt==0){ cout<<ans<<endl; return 0; } ans=sum=vec[0]; for(int i=1;i<n;i++){ if(sum+vec[i]>=0) sum+=vec[i]; else sum=max(0,vec[i]); //i번째 포함 유무 ans=max(ans,sum); } cout<<ans<<endl; return 0; } | cs |
'알고리즘(BOJ) > DP' 카테고리의 다른 글
백준 10844번 - 쉬운 계단 수 (0) | 2018.01.25 |
---|---|
백준 1010번 - 다리놓기 (0) | 2018.01.25 |
백준 2156번 - 포도주 시식 (0) | 2018.01.25 |
백준 11726번 - 2xN 타일링 (0) | 2018.01.25 |
백준 2193번 - 이친수 (0) | 2018.01.25 |