알고리즘 문제풀이
[Algospot] 알고스팟 크리스마스 인형 문제풀이(CHRISTMAS)
도리컴
2023. 2. 11. 00:52
반응형
문제 핵심 1번은 부분합, 2번은 동적 계획법
1. Psum[T] = Psum[H-1]임을 이용, count변수를 통해 각 Psum값이 몇 번 나오는지 저장, 각 count값 중 2 이상인 것들에 대해 2개 무작위로 뽑는 경우의 수의 합이 한 번 주문하는 방법 수와 같음
2. [0]~[i]번까지 주문할 때 최대 주문 수 -> [0]~[i-1]까지 주문하는 경우에서 [i]상자를 구매하는 경우와 안하는 경우를 비교 / 구매하는 경우에는 Psum값이 같은 곳 중 최근에 본 위치를 알고있어야 하므로, prev[Psum[i]]를 사용.
1. Psum[T] = Psum[H-1]임을 이용, count변수를 통해 각 Psum값이 몇 번 나오는지 저장, 각 count값 중 2 이상인 것들에 대해 2개 무작위로 뽑는 경우의 수의 합이 한 번 주문하는 방법 수와 같음
2. [0]~[i]번까지 주문할 때 최대 주문 수 -> [0]~[i-1]까지 주문하는 경우에서 [i]상자를 구매하는 경우와 안하는 경우를 비교 / 구매하는 경우에는 Psum값이 같은 곳 중 최근에 본 위치를 알고있어야 하므로, prev[Psum[i]]를 사용.
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
#include<iostream>
#include<vector>
#include<algorithm>
#define MOD 20091101
using namespace std;
/*
K명에게 인형을 사주는데,
N개의 인형상자가 한 줄로 진열되어있음
각 상자에 1개 이상의 인형이 있음
각 상자에 번호가 있고, 주문형태는 H번부터 T번 상자까지(H<=T)
한번 주문 시마다 각 상자 인형 꺼내서 각각 K명에게 분배하고, 남는 인형이없도록 함
1. 한 번 주문할 수 있다면, 가능한 주문 방법 수는?
2. 여러 번 주문할 수 있다면, 주문이 안겹치게 최대 몇번 주문가능?
(주문이 겹침 = 두 주문에 같은 번호의 인형상자 포함)
<입력>
T(테스트케이스, <=60)
N K(둘다 1~10만)
1~N번 상자까지 각 인형상자에 들어있는 인형개수 Di
(i는 1~N, Di는 1~10만)
<출력>
1번답, 2번답을 한 줄에 하나의 빈칸으로 나누어 출력
(1번답은 % 20091101 계산해서 출력)
*/
int Psum[100001]; //Psum[i] : 0번부터~(i-1)번 상자까지의 인형 합 % K
int N, K;
int oneTake() { //한 번 주문 가능한 모든 경우의 수
//(a, b)가 주문가능하려면, (Psum[b] - Psum[a-1]) % K = 0 즉, Psum[b] % K = Psum[a-1] % K이어야 한다는 뜻
//즉, Psum값이 서로 같은 두 인덱스값을 뽑으면 그게 하나의 경우이므로
//각 Psum값이 몇개나오는지 카운트하는 변수 vector<int> count(K, 0); 을 만들어야 함
//count[i] = Psum[x]=i인 x의 개수
//-> count를 뒤져보며, 값이 2 이상인 값 x에 대해 모두 xC2를 계산해서 더하면 됨(x개 중 2개를 임의로 뽑는 경우의 수)
vector<long long> count(K, 0);
for (int i = 0; i <= N; i++) {
count[Psum[i]]++;
}
long long int res = 0; //결과값
for (int i = 0; i < K; i++) {
if (count[i] >= 2) {
res += (count[i] * (count[i] - 1) / 2) % MOD;
}
}
return res % MOD;
}
int manyTake() { //[0]~[a]까지에서 여러번 살 수 있을 때 안겹치는 최대 구매횟수
vector<int> prev(K, -1); //prev[i] : Psum에서 가장 마지막에 i 값을 본 인덱스위치 저장
vector<int> DP(N+1, 0); //DP[i] : [0]~[i-1]까지 가장 많이 구매하는 경우의 수
DP[0] = 0;
for (int i = 0; i <= N; i++) {
if (i > 0) DP[i] = DP[i - 1];
int location = prev[Psum[i]]; //이전 위치
if (location != -1) { //이전 위치에서 구매 가능한 경우를 찾은 경우 -> 해당 위치에서 구매할 경우와 그렇지 않은 경우 비교해서 DP에 저장
DP[i] = max(DP[i], DP[location] + 1);
}
//위치 기록
prev[Psum[i]] = i;
}
return DP.back();
}
int main() {
int T; cin >> T;
while (T--) { //testcase
cin >> N >> K; //상자 수, 사람 수
for (int i = 0; i < 100000; i++) {
Psum[i] = 0;
}
vector<int> box(N + 1); //각 상자(인형 수 저장)
Psum[0] = 0;
for (int i = 1; i <= N; i++) {
int a; cin >> a; box[i - 1] = a;
Psum[i] = (Psum[i - 1] + box[i - 1]) % K;
} //Psum 입력 완료
cout << oneTake() << " ";
cout << manyTake() << endl;
}
return 0;
}
|
cs |
반응형