2017년 8월 29일 화요일

2878 캔디캔디

2878 캔디캔디 https://www.acmicpc.net/problem/2878
3661 생일선물 https://www.acmicpc.net/problem/3661

1. 캔디캔디
사탕 $M$개로 친구들에게 나누어줬을 때 친구들이 원하는 사탕을 받지 못한 수의 제곱만큼 분노가 찬다.
이 분노들의 합을 최소화 시키는 문제이다.

굉장히 어렵게 느껴졌었다.
친구$i$가 얻고 싶은 사탕을 $candy_{i}$라 하면 $min \sum_{i=1}^{N} (candy_{i} - give_{i})^2$, $\sum_{i=1}^{N} give_{i} = M$이다.

줄 수 있는 사탕과 친구들이 얻고싶은 사탕은 정해져있으므로 못주는 사탕 또한 정해져있다.

못주는 사탕을 잘 분배해서 최솟값을 만들면 된다.
코드를 먼저 보겠다.
#include <cstdio>
#include <algorithm>
using namespace std;
int M, N;
int candy[100001];
int main() {
    scanf("%d%d"&M, &N);
    long long sum = -M;
    for (int n = 0;n < N;n++scanf("%d"&candy[n]), sum += (long long)candy[n];
    sort(candy, candy + N);
    long long ans = 0;
    for (int n = 0;n < N;n++) {
        long long w = min((long long)candy[n], sum / (N - n));
        ans += w*w;
        sum -= w;
    }
    printf("%lld\n", ans);
    return 0;
}
cs
현재 sum에는 못주는 사탕의 개수가 들어있다.
잘 분배한다는 방법은 사탕을 많이 얻고싶어하는 사람에게 더 많이 주면 된다.
그 방법을 위와같이 정렬한후에 남은 사람 수로 나누어 그 값을 계산한다.

2. 생일선물
선물 가격이 P이고 N명의 사람들이 낼 수 있는 최대금액들이 정해져있다.
각 사람이 내는 금액과 P/N의 최댓값을 최소화 시키도록 하는 문제다.

위 문제처럼 정렬한 후에 적당하게 나눠주면 풀 수있다.
최댓값이 동일하면 돈을 많이 낼 수 있는사람이 더 내고 
이마저도 동일하면 리스트 앞에 있는사람이 돈을 더 낸다는 조건이 있다.
코드를 먼저 보겠다.
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int N, P;
int ans[101];
pair<intint> g[101];
int main() {
    int T;
    scanf("%d"&T);
    while (T--) {
        scanf("%d%d"&P, &N);
        for (int n = 0;n < N;n++) {
            scanf("%d"&g[n].second), g[n].first = n;
        }
        sort(g, g + N, [](pair<intint> &a, pair<intint> &b)->bool {
            return a.second == b.second ? a.first > b.first : b.second > a.second;
        });
        for (int n = 0;n < N;n++) {
            ans[g[n].first] = min(g[n].second, P / (N - n));
            P -= ans[g[n].first];
        }
        if (P != 0printf("IMPOSSIBLE\n");
        else {
            for (int n = 0;n < N;n++printf("%d ", ans[n]);
            printf("\n");
        }
    }
    return 0;
}
cs
금액을 내림차순, 인덱스값도 내림차순으로 정렬하고
지속적으로 P값을 빼주면서 남은사람수로 나누어준다.

인덱스를 내림차순으로 정렬하는 이유는 동일한 금액에 대해서 뒤쪽에 있는 값이 더 작다.
(즉, 앞의 리스트사람이 많이 줄어듬 = 많이 냄)

댓글 6개:

  1. 많이 푸시면 익숙해 지실 듯 싶네요.
    이분이 어렵게 나오면 극혐스러울 정도로 어렵게 나오더라고요.
    그래프 이론이랑 섞이거나.. 기하랑 섞이거나 하면.. 끔찍합니다.

    캔디캔디는 '숨겨진 이분' 치고는 그나마 나은 편이긴 합니다..
    캔디가 더 많이 필요한 사람에게 1개씩 주면 분노가 많이 줄어든다는 걸 이용해서요..
    1637번 날카로운 눈 역시 이분탐색이고.. f(x) 자체는 이 친구보다 간단하긴 하지만..
    난도는 더 어렵더라고요..

    답글삭제
    답글
    1. PS처음할때는 greedy랑 이분탐색이 매우 쉬운분야인줄 알았는데
      가면갈수록 생각을 더 이끌어내야되고 어려운 쪽이 많네요 ㅠㅠ
      많이 푸는수밖에 없군요

      삭제
    2. 네.. 많이 푸시는 수밖에 없어요.. ㅠㅠ
      사실 3197이.. 유니온 파인드 비슷하게 구현하는 게 정해일 거 같긴 한데요.
      익숙해 지신다면 이분으로 구현해볼까? 라는 생각도 조금은 할 수 있지요.
      이분 중에서 이런 유형도 꽤 나오긴 했습니다.

      처음에 이분 우습게 봤다가 coci #6번 (아마 2990번일 겁니다) 보고..
      만만한게 절대 아니구나.. 했습니다.

      그나저나 설대 div1. 생각 외로 어렵더라고요.
      슬라임(H) + 셔틀버스 + 관악산 + 정육면체 빼고는.. 구현을 못하겠네요..

      삭제
    3. 와ㄷㄷ 전 관악산빼고는 손도 못댔는데 ㅋㅋ...
      좋은 조언 감사합니다. 더 열심히 해야겠네요 ㅎㅎ

      삭제
    4. 그런데. dp는 진짜 어렵더라고요..
      그거 역시 제가 많이 안 풀어봐서 그런 것일수도 있긴 합니다..

      충남대의 우유도시도 O(n^2)짜리 고민하느라 한참 걸리고..
      5 ~ 9번 가량 틀린 거 같네요. 이상하게 어렵더라고요.. 빗물에서 말리고..

      플로우 같은 다른 알고리즘도 해야 하는데.. 계속 몇 가지만 파다 보니..
      그냥 대회에 나오는 것도 안하긴 했습니다.
      (이래서 종만북이 좋은가 봅니다.)

      삭제
    5. dp도 어려워요 ㅠㅠ
      우유도시 우유축제 풀고 복붙하면서 잘못코딩해서 좀 틀렸었던 기억이 ㅋㅋ;;

      삭제