본문 바로가기

PS

Codeforces Round #642 (Div. 3)

딥3이다. 쉬운 문제들만 있기 때문에 연습하기에 좋다.

또 뭔 문제를 풀지 모를 때 문제를 알아서 던져줘서 좋기도 하다.

민트 이하한테는 떡상의 기회도 되는데, 나는 민트가 아니니까 해당사항이 없다.

 

풀이를 쓰고 있는 시점에서의 친창 스코어보드
제출기록

A. Most Unstable Array

 

A번이니만큼 간단한 문제다.

일단 N=1이면 답이 0이고 N=2일 때 답이 m인 것은 매우 자명하다.

N>=3일 때는 답은 m*2이다. 직관적으로 보일 수 있지만 증명해보자.

0 m 0 0... 꼴로 답이 m*2이게 할 수 있다. 그럼 답이 m*2를 초과할 수 없음을 증명한다.

답을 c1*a1+c2*a2+c3*a3+c4*a4+...+cn*an 꼴로 나타낼 수 있다. 이때 -2<=ci<=2임이 자명하다. ci에 영향을 끼칠 수 있는 항은 |a[i-1]-a[i]|과 |a[i]-a[i+1]| 뿐이기 때문이다.

그러면 식을 -2*(a1+a2+a3+...+an)+((c1+2)*a1+(c2+2)*a2+...+(cn+2)*an) 꼴로 나타낸다. -2*(a1+a2+..+an)=-2m이다. ((c1+2)*a1+(c2+2)*a2+...+(cn+2)*an)<=4*a1+4*a2+4*a3+4*a4+...+4*an=4*m이다. 그럼 두 개를 더한 것은 -2*m 이상 2*m 이하로 2*m 이하이다.

 

#include <bits/stdc++.h>
using namespace std;
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        int n,m;
        scanf("%d %d\n",&n,&m);
        if (n==1) {
            printf("0\n");
        }
        else if (n==2) {
            printf("%d\n",m);
        }
        else {
            printf("%d\n",m*2);
        }
    }
}

B. Two Arrays And Swaps

 

아주 자명한 그리디이다. a의 가장 작은 수와 b의 가장 큰 수를 스왑해주는 연산을 k번 수행하자. a의 가장 작은 수가 b의 가장 큰 수보다 크면 수행을 멈춘다. 구현은 우선순위 큐를 이용하였다.

 

#include <bits/stdc++.h>
using namespace std;
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        int n,k;
        scanf("%d %d\n",&n,&k);
        priority_queue<int,vector<int>,greater<int>> a;
        priority_queue<int> b;
        for(int i=0;i<n;i++) {
            int x;
            scanf("%d",&x);
            a.push(x);
        }
        for(int i=0;i<n;i++) {
            int x;
            scanf("%d",&x);
            b.push(x);
        }
        for(int i=0;i<k;i++) {
            int mini=a.top();
            int maxi=b.top();
            if (mini>maxi) {
                break;
            }
            a.pop();
            b.pop();
            b.push(mini);
            a.push(maxi);
        }
        int sum=0;
        for(int i=0;i<n;i++) {
            sum+=a.top();
            a.pop();
        }
        printf("%d\n",sum);
    }
}

C. Board Moves

 

직관적으로 중심의 점으로 모든 점을 모으는 것이 최적임을 알 수 있다. 증명은 생략한다.

저 식이 어떻게 성립되는지는 잘 생각해보자.

#include <bits/stdc++.h>
using namespace std;
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        long long n;
        scanf("%lld\n",&n);
        long long ret=0;
        for(long long i=1;i<n;i+=2) {
            ret+=n*n-i*i;
        }
        printf("%lld\n",ret);
    }
}

 

대회 중에 푼 코드.

 

D. Constructing the Array

 

길이와 시작점을 넣어두고 적당히 우선순위 큐로 관리해주자. 그럼 끝이다.

 

#include <bits/stdc++.h>
using namespace std;
 
int n;
typedef pair<int,int> P;
int cnt=0;
int ret[300005];
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        cnt=0;
        scanf("%d\n",&n);
        priority_queue<P> pq;
        pq.push(P(n-1,0));
        while (!pq.empty()) {
            int l=-pq.top().second;
            int r=l+pq.top().first;
            int mid=(l+r)/2;
            pq.pop();
            ret[mid]=++cnt;
            if (l<=mid-1) {
                pq.push(P(mid-1-l,-l));
            }
            if (mid+1<=r) {
                pq.push(P(r-mid-1,-mid-1));
            }
        }
        for(int i=0;i<n;i++) {
            printf("%d ",ret[i]);
        }
        printf("\n");
    }
}

 

 

E. K-periodic Garland

 

인덱스의 나머지가 x인 부분에만, k 간격으로 1이 존재해야 한다.

먼저 모든 수를 0으로 바꾼다고 가정해보자. 그러면 필요한 연산 횟수는 1의 개수만큼이다.

이제 이걸 베이스로 생각한다.

여기서 나머지 x를 고정하고 생각해보자.

나머지가 x인 인덱스들을 나열하면 우리는 k 간격으로 1이 존재하게 하는 것을 적절히 구간을 정하는 것으로 환원할 수 있다.

그리고 구간에 대한 수를 1로 바꿀 때 드는 비용에 대해 알아보자.

구간에 있는 0은 원래 연산이 필요없었지만 필요해져서 1의 연산을 추가로 요구한다. 구간에 있는 1은 원래 연산이 필요했지만 필요없어져서 1의 연산을 절약할 수 있다.

이제 이걸 minimum subarray 문제로 다시 환원할 수 있다.

시간복잡도는 O(k*n/k)=O(n)

 

#include <bits/stdc++.h>
using namespace std;
 
int arr[1000000];
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        int n,k;
        scanf("%d %d\n",&n,&k);
        int ret=n;
        int sum=0;
        for(int i=0;i<n;i++) {
            scanf("%1d",&arr[i]);
        }
        for(int i=0;i<n;i++) {
            sum+=arr[i];
        }
        ret=sum;
        for(int i=0;i<k;i++) {
            vector<int> v;
            for(int j=i;j<n;j+=k) {
                if (arr[j]==1) {
                    v.push_back(-1);
                }
                else {
                    v.push_back(1);
                }
            }
            int val=0;
            for(int j=0;j<v.size();j++) {
                val=min(v[j],val+v[j]);
                ret=min(ret,sum+val);
            }
        }
        printf("%d\n",ret);
    }
}

 

 

F. Decreasing Heights

 

일단 문제가 약간 복잡하니 살짝 바꾼다.

약간의 관찰을 한다.

"한 번 이동할 때 i+j의 값은 무조건 1씩 증가한다."

그러면 a[i][j]=a[i][j]-i-j;를 시행한다.

이제 a[i][j]가 모두 같은 경로를 만드는 것이 목표가 된다.

 

그럼 이제 경로상에서 모두 같은 a[i][j]를 x로 고정하다.

이때 최적의 후보인 x는 a[i][j]에 등장하는 n*m개의 수들이다.

그러면 O(nm) dp를 nm번 돌리자.

dp식은 다음과 같이 정의할 수 있다.

a[i][j]<x인 경우:

dp[i][j]=INF

a[i][j]>=x인 경우:

dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j]-x

dp[i][j]는 경로상의 값이 모두 x이게 (i,j)까지 경로를 잇는데 필요한 비용이다.

O(n^2m^2)에 풀 수 있다.

 

#include <bits/stdc++.h>
using namespace std;
 
long long arr[100][100];
long long dp[100][100];
const long long INF=1e18;
 
int n,m;
 
bool valid(int x,int y) {
    return x>=0&&x<n&&y>=0&&y<m;
}
 
int main(void) {
    int tc;
    scanf("%d\n",&tc);
    for(int t=0;t<tc;t++) {
        scanf("%d %d\n",&n,&m);
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                scanf("%lld ",&arr[i][j]);
                arr[i][j]-=i+j;
            }
            scanf("\n");
        }
        long long ret=INF;
        for(int one=0;one<n;one++) {
            for(int two=0;two<m;two++) {
                long long val=arr[one][two];
                if (arr[0][0]<val) {
                    continue;
                }
                for(int i=0;i<n;i++) {
                    for(int j=0;j<m;j++) {
                        dp[i][j]=INF;
                    }
                }
                dp[0][0]=arr[0][0]-val;
                for(int i=0;i<n;i++) {
                    for(int j=0;j<m;j++) {
                        if (arr[i][j]>=val) {
                            if (valid(i-1,j)) {
                                dp[i][j]=min(dp[i][j],dp[i-1][j]+arr[i][j]-val);
                            }
                            if (valid(i,j-1)) {
                                dp[i][j]=min(dp[i][j],dp[i][j-1]+arr[i][j]-val);
                            }
                        }
                    }
                }
                ret=min(ret,dp[n-1][m-1]);
            }
        }
        printf("%lld\n",ret);
    }
}

 

'PS' 카테고리의 다른 글

NYPC 2020 예선 풀이  (1) 2020.09.06
2020.06.28 Problem Solving  (2) 2020.06.28
BAPC 2019  (0) 2020.05.11
백준 18195번 정점 찾기  (0) 2020.05.10
BAPC 2016  (0) 2020.05.06