백준: Silver2⑤ - 1421, 1455, 1535, 1541

3 분 소요


</br> 이제 좀만 더 하면 실버 1
</br>

1421: 나무꾼 이다솜

https://www.acmicpc.net/problem/1421

나무를 일정한 크기로 잘라서 최대 수익 얻기
</br>

    int maxL = 0;
    for(int i = 0; i < n; i++){
        int a;
        cin >> a;
        maxL = max(maxL, a);
        v.push_back(a);
    }

    for(int i = 0; i < n; i++){
        for(int j = 1; j < v[i]+1; j++){
            int cut = (v[i]-1)/j;
            long long money = 0;
            for(int k = 0; k < cut+1; k++){
                long long t = ( k + ( (v[i]-k*j)==j ) )*j*w - c*k;
                money = max(money, t);
            }
            dp[j] = max(dp[j], dp[j] + money);
        }
    }

    cout << *max_element(dp, dp + maxL+1) << endl;

dp로 풀 수 있다.
수익[길이 L] = max( 수익[길이 L], 수익[길이 L] + 현재 나무를 길이 L로 잘랐을 때 최대 수익 )
“현재 나무를 L로 잘랐을 때 최대 수익”은 for문으로 0번 잘랐을 때 ~ 최대한 다 잘랐을 때까지 하나 씩 다 확인해 구할 수 있다.

k번 잘랐을 때의 수익은 ( k + ( (v[i]-k*j)==j ) )*j*w - c*k인데, 잘라서 딱 떨어지면 k+1개의 나무 도막, 그게 아니면 k개의 나무 도막을 얻기 때문에 k + ( (v[i]-k*j)==j )과 같이 식을 썼다.
</br>

1455: 뒤집기 II

https://www.acmicpc.net/problem/1455

모든 동전을 앞면으로 뒤집는 최소 횟수 구하기

    int cnt = 0;
    for(int i = n-1; i > -1; i--){
        for(int j = m-1; j > -1; j--){
            if( mmap[i][j] ){
                cnt++;
                for(int ii = 0; ii <= i; ii++){
                    for(int jj = 0; jj <= j; jj++){
                        mmap[ii][jj] = 1 - mmap[ii][jj];
                    }
                }
            }
        }
    }
    cout << cnt << endl;

짱 간단… 이거 왜 실버2에 있지
전에 한 풀이 1080번(https://cyj893.github.io/algorithm/Algorithm8_2/)에서와 비슷하게, 0과 1이므로 여러 번 뒤집는 건 의미가 없고 한 번만 뒤집도록 한다.
전부 0이 되어야 하는데, 왼쪽 위 구석 ~ 현재 위치 까지를 모두 뒤집으므로 오른쪽 아래 구석부터 탐색하면 된다.
</br>

1535: 안녕

https://www.acmicpc.net/problem/1535

체력 안에서 최대의 기쁨 얻기

방법 1.

void func(int now){
    if( hp <= 0 ){
        return;
    }
    ans = max(ans, joy);
    for(int i = now+1; i < n; i++){
        joy += j[i];
        hp -= l[i];
        func(i);
        joy -= j[i];
        hp += l[i];
    }
}

백트래킹을 이용할 수 있겠더라. 사람 수가 20 이하이므로 20C0 ~ 20C20까지 모든 조합의 경우의 수 합은 2^20 = 1,048,576 이니까 2초 안에 충분히 돌아간다.
또 체력이 다 떨어지면 return 해 주기 때문에 웬만하면 그보다 더 적게 걸릴 거다.
</br>

방법 2.

    for(int i = 1; i < n+1; i++){
        for(int j = 1; j < 101; j++){
            if( j + L[i] < 101  ) dp[i][j] = max(dp[i-1][j], dp[i-1][j+L[i]] + J[i]);
            else dp[i][j] = dp[i-1][j];
        }
    }

    cout << *max_element(dp[n]+1, dp[n]+ 101) << endl;

근데 너무 dp 느낌이라서 그렇게도 풀었다.
dp[현재 사람][체력] = max(dp[현재 사람 넣기 전][체력], dp[현재 사람 넣기 전][체력+닳는체력] + 얻는 기쁨)

dp[n]에 99, 99, 70, 70, 70, 65, …, 0 이런 식으로 체력 i에서 획득 가능한 최대의 기쁨이 저장이 되어 있을 거라서, 마지막에 max_element를 안 하고 그냥 dp[n][1]을 출력해도 될 것 같다.
</br>

1541: 잃어버린 괄호

https://www.acmicpc.net/problem/1541

식에 괄호 넣어서 최솟값 만들기

int readNum(){
    int k = 0;
    for( ; i < s.size(); i++){
        if( s[i] == '+' || s[i] == '-' ) break;
        if( k == 0 ) k += s[i]-'0';
        else k = k*10 + s[i] - '0';
    }
    i--;
    return k;
}

// in main()
    s = '+' + s;
    int ans = 0;
    for( ; i < s.size(); i++){
        if( s[i] == '-' ) break;
        if( s[i] == '+' ) continue;
        ans += readNum();
    }
    for( ; i < s.size(); i++){
        if( s[i] == '-' || s[i] == '+' ) continue;
        ans -= readNum();
    }
    cout << ans << endl;

간단한 그리디다
좀 보니까 그냥 쭉 더해 가다가, -가 한 번이라도 나왔으면 다 빼주면 된다.

수 읽기 처리만 잘하면 된다.
</br>


드디어 실버 1이다~~ 일주일 안에 골5는 가겠다
</br>

댓글남기기