최대 열정 팀 구성 알고리즘

문제 설명

n명의 참가자가 각각 능력치와 열정도를 가지고 있을 때, 팀 내 최대와 최소 능력치 차이가 주어진 X 이하인 조건에서 팀 전체 열정도의 합을 최대화하는 문제입니다.

입력 형식

첫 줄: 참가자 수 n
다음 n줄: 각 참가자의 능력치와 열정도
마지막 줄: 허용 가능한 최대 능력치 차이 X

출력 형식

조건을 만족하는 최대 열정도 합 출력

데이터 범위

  • 30% 테스트 케이스: 1 ≤ n ≤ 100
  • 60% 테스트 케이스: 1 ≤ n ≤ 104
  • 100% 테스트 케이스: 1 ≤ n ≤ 105, 1 ≤ X ≤ 109, 능력치와 열정도는 1~109

예제

입력:
5
10 21
20 34
30 27
40 89
50 54
20

출력:
170

해결 방법 1: 완전 탐색(DFS)

모든 가능한 조합을 탐색하는 방법으로, n이 작을 때만 사용 가능합니다. 시간 복잡도는 O(n!)입니다.

#include <iostream>
#include <algorithm>
using namespace std;

struct Participant {
    int ability;
    int passion;
};

Participant arr[100];
long maxPassion;
int diffLimit, n;

void explore(int idx, long currentSum, int minAbil, int maxAbil) {
    if(idx == n) {
        if(currentSum > maxPassion) maxPassion = currentSum;
        return;
    }
    for(int next = idx; next < n; next++) {
        int newMin = min(minAbil, arr[next].ability);
        int newMax = max(maxAbil, arr[next].ability);
        if(newMax - newMin > diffLimit) continue;
        explore(next + 1, currentSum + arr[next].passion, newMin, newMax);
    }
    if(currentSum > maxPassion) maxPassion = currentSum;
}

int main() {
    cin >> n;
    for(int i = 0; i < n; i++)
        cin >> arr[i].ability >> arr[i].passion;
    cin >> diffLimit;
    explore(0, 0, 1e9+1, 0);
    cout << maxPassion;
    return 0;
}

해결 방법 2: 정렬 및 이중 루프

능력치 기준으로 정렬 후 각 시작점에서 가능한 최대 지점까지 열정도를 누적합니다. 시간 복잡도 O(n²)로 중간 규모 데이터까지 처리 가능합니다.

#include <iostream>
#include <algorithm>
using namespace std;

struct Member {
    int capability;
    int enthusiasm;
};

bool compare(Member m1, Member m2) {
    return m1.capability < m2.capability;
}

int main() {
    int n, maxDiff;
    cin >> n;
    Member team[100000];
    for(int i = 0; i < n; i++)
        cin >> team[i].capability >> team[i].enthusiasm;
    cin >> maxDiff;
    
    sort(team, team + n, compare);
    
    long maxTotal = 0;
    for(int start = 0; start < n; start++) {
        long current = 0;
        for(int end = start; end < n; end++) {
            if(team[end].capability - team[start].capability > maxDiff) break;
            current += team[end].enthusiasm;
        }
        maxTotal = max(maxTotal, current);
    }
    cout << maxTotal;
    return 0;
}

해결 방법 3: 슬라이딩 윈도우

정렬 후 두 포인터 기법으로 효율적으로 구간을 관리하며 최적해를 찾습니다. 시간 복잡도 O(n log n)으로 대규모 데이터 처리에 적합합니다.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct Competitor {
    int skill;
    int zeal;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int playerCount, maxGap;
    cin >> playerCount;
    vector<Competitor> players(playerCount);
    for(int i = 0; i < playerCount; i++)
        cin >> players[i].skill >> players[i].zeal;
    cin >> maxGap;
    
    sort(players.begin(), players.end(), [](const Competitor& a, const Competitor& b) {
        return a.skill < b.skill;
    });
    
    long windowSum = 0, maxZealSum = 0;
    int left = 0;
    
    for(int right = 0; right < playerCount; right++) {
        windowSum += players[right].zeal;
        while(players[right].skill - players[left].skill > maxGap) {
            windowSum -= players[left].zeal;
            left++;
        }
        maxZealSum = max(maxZealSum, windowSum);
    }
    cout << maxZealSum;
    return 0;
}

태그: 정렬 두 포인터 슬라이딩 윈도우 최적화

6월 17일 22:39에 게시됨