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

 

18113번: 그르다 김가놈

첫 번째 줄에 손질해야 하는 김밥의 개수 N, 꼬다리의 길이 K, 김밥조각의 최소 개수 M이 주어진다. (1 ≤ N ≤ 106, 1 ≤ K, M ≤ 109, N, K, M은 정수) 두 번째 줄부터 김밥의 길이 L이 N개 주어진다.

www.acmicpc.net

김밥의 꼬다리를 K * 2 만큼 잘라낸 김밥을 손질된 김밥이라고 하며, K * 2보다 작은 김밥은 K만큼만 잘라낸다고 합니다.

그 손질된 김밥을 길이가 P인 조각으로 잘라내 최소 M개의 조각을 만드려고 합니다.

-> 최소 M개의 조각을 만들기 위한 최대 P를 구하는 문제로 파라매트릭 서치를 이용합니다.

 

파라매트릭 서치는 이분탐색과 큰 차이는 없으며,

이분탐색이 정확히 M인 값을 구하는 반면, 파라매트릭 서치는 M에 가장 가까운 최적의 값을 구하는 것입니다.

 

 

이 문제는 두 가지 방법을 선택하여 해결할 수 있습니다.

먼저, 모든 김밥에 대해 최적의 P를 구하는 방법입니다.

이 방법은 입력을 그대로 다 받아놓고, 파라매트릭 서치 과정에서 K * 2 or K를 빼는 식이 포함되는 방법입니다.

 

아니면, 애초에 K보다 작거나 K * 2와 같은 길이의 김밥을 먼저 제외하여 최적의 P를 구하는 방법입니다.

이 방법은 입력을 받을 때 미리 K * 2 or K를 빼는 식이 포함되는 방법입니다.

이 과정에서 꼬다리를 제거했을 때 길이가 0 이하가 되는 김밥을 제외합니다.

두 방법 모두 AC를 받는데는 문제가 없으나 두번째 방법이 더 적은 갯수로 구할 수 있으므로 시간상 이득을 볼 수 있습니다.

 

파라매트릭 과정에서 필요없는 김밥을 제외
입력 과정에서 필요없는 김밥을 제외

두 방법으로 제출했을 때 확실히 미리 제외한 방법이 시간상 효율적이었습니다.

 

파라매트릭 서치 과정에서는

l, r은 김밥조각의 길이인 P의 범위이며, 김밥을 m으로 나눈 몫을 카운팅한 값이 M 이상이 되는 최적의 해를 구해주시면 되겠습니다.

 

 

[필요없는 김밥을 제외하지 않은 코드]

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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
vector<int> list;
int N, K, M, l, r;
 
void func() {
    int ans = -1;
    l = 1;
    while (l <= r) {
        int m = (l + r) >> 1;
 
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            if (list[i] <= K) continue;
 
            if (list[i] >= K * 2) cnt += ((list[i] - K * 2/ m);
            else cnt += ((list[i] - K) / m);
        }
 
        if (cnt >= M) {
            ans = m;
            l = m + 1;
        }
        else {
            r = m - 1;
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    int x;
    cin >> N >> K >> M;
    for (int i = 0; i < N; i++) {
        cin >> x;
 
        list.push_back(x);
        r = max(r, x);
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

 

 

[처음부터 필요없는 김밥을 제외한 코드]

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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
vector<int> list;
int N, K, M, l, r;
 
void func() {
    int ans = -1;
    l = 1;
    while (l <= r) {
        int m = (l + r) >> 1;
 
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            cnt += (list[i] / m);
        }
 
        if (cnt >= M) {
            ans = m;
            l = m + 1;
        }
        else {
            r = m - 1;
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    int x;
    cin >> N >> K >> M;
    for (int i = 0; i < N; i++) {
        cin >> x;
        if (x <= K || x == K * 2continue;
 
        int sub = x;
        if (x < K * 2) sub -= K;
        else sub -= (K * 2);
 
        list.push_back(sub);
        r = max(r, sub);
    }
 
    N = list.size();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > binarysearch' 카테고리의 다른 글

boj 27977 킥보드로 등교하기  (4) 2023.04.22
boj 2295 세 수의 합  (0) 2022.06.28
boj 2110 공유기 설치  (0) 2021.04.13
boj 7453 합이 0인 네 정수  (0) 2021.01.22
boj 2143 두 배열의 합  (0) 2021.01.22

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

 

2281번: 데스노트

첫째 줄에 n(1 ≤ n ≤ 1,000), m(1 ≤ m ≤ 1,000)이 주어진다. m은 노트의 가로 칸의 개수(폭, 너비)이다. 다음 n개의 줄에는 각 사람의 이름의 길이가 노트에 적어야 할 순서대로 주어진다. 각 길이는 m

www.acmicpc.net

노트에 이름을 순서대로 적기 위한 조건은 아래와 같습니다.

  1. 위에서 아래로 적습니다.
  2. 같은 줄에서는 왼쪽에서 오른쪽으로 적습니다.
  3. 이름 사이에는 한 칸의 빈 칸이 있습니다.
  4. 한 사람의 이름은 한 줄에만 적어야 하며, 해당 줄에 사람의 이름을 다 적지 못하면 다음 줄에 적어야 합니다.
  5. 한 줄을 넘어가는 길이의 이름은 주어지지 않습니다.

위 조건을 지키면서 이름을 적으며, 마지막 줄을 제외한 모든 줄에서 끝에 남은 칸의 갯수의 제곱을 더한 값의 최소를 구하는 문제입니다.

 

dp[idx][len]: idx번째 이름을 적을 차례이고, idx - 1번째까지 len의 공간을 사용했을 때의 최솟값

여기서 선택할 수 있는 방법은

  1. 현재 줄을 빈 칸으로 놔두고 다음 줄에 이름을 적는다.
  2. 현재 줄에 남은 칸이 이름의 길이만큼 남았다면 현재 줄에 이름을 적는다.

이렇게 두 가지 있습니다.

1번은 모든 경우에 할 수 있으며, 2번은 남은 공간을 파악하여 구하도록 합니다.

 

 

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
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAX 1001
using namespace std;
 
int dp[MAX][MAX], list[MAX];
int N, M;
 
int solve(int idx, int len) {
    if (idx == N) return 0;
 
    int& ret = dp[idx][len];
    if (ret != -1return ret;
    
    ret = (M - len + 1* (M - len + 1+ solve(idx + 1, list[idx] + 1);
    if (len + list[idx] <= M) {
        ret = min(ret, solve(idx + 1, len + list[idx] + 1));
    }
 
    return ret;
}
 
void func() {
    memset(dp, -1sizeof(dp));
    cout << solve(00<< '\n';
}
 
void input() {
    cin >> N >> M;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 14450 Hoof, Paper, Scissors (Gold)  (0) 2022.12.30
boj 14453 Hoof, Paper, Scissors (Silver)  (0) 2022.12.30
boj 2015 수들의 합 4  (0) 2022.08.12
boj 21923 곡예 비행  (0) 2022.06.10
boj 12978 스크루지 민호 2  (0) 2022.06.08

https://whyeskang.com/258

 

Postman을 이용한 File, Dto 동시 Post요청

보통 Controller에서 Dto를 받을 때는 @RequestBody를 주로 사용합니다. 그리고 File을 받을 때는 MultipartFile 객체를 사용하며, @RequestParam을 사용합니다. 하지만 File과 Dto를 같이 받기 위해서는 @RequestPart라

whyeskang.com

작년에 이어 File, Dto 동시 요청 2탄입니다.

 

이전 글에서는 단일 File과 Dto를 동시에 요청받았습니다.

이번 글에서는 File과 Dto의 List를 Postman을 이용하여 요청하는 것을 다루려고 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
@Data
@NoArgsConstructor
public class User {
    @Id
    String userId;
    String password;
    String profileImageUrl;
 
    public User(UserSignUpReq userSignUpReq) {
        this.userId = userSignUpReq.getUserId();
        this.password = userSignUpReq.getPassword();
    }
}
cs

User Entity입니다.

테스트 용도이니 간단하게만 작성해줍니다.

1
2
3
4
5
@Data
public class UserSignUpReq {
    String userId;
    String password;
}
cs

RequestDto 또한 간단하게 해줍니다.

 

 

1
2
3
4
5
6
7
8
9
10
@PostMapping("/signup")
public ResponseEntity<List<UserGetRes>> signUpUser(
       @RequestPart(value = "multipartFileList") List<MultipartFile> multipartFileList,
       @RequestPart(value = "userSignUpReqList") List<UserSignUpReq> userSignUpReqList) {
    return new ResponseEntity<>(
            userService.loginUser(
                    multipartFileList,
                    userSignUpReqList.stream().map(userSignUpReq -> new User(userSignUpReq)).collect(Collectors.toList())),
            HttpStatus.OK);
}
cs

그 다음 요청을 받을 Conrtoller입니다.

어노테이션은 @RequestPart로 이전과 같으며, 요청을 정상적으로 받았는지 확인하기 위해 List<ResDto>를 리턴합니다.

 

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
@Override
public List<UserGetRes> loginUser(List<MultipartFile> multipartFileList, List<User> userList) {
    try {
        String separ = File.separator;
        String today = new SimpleDateFormat("yyMMdd").format(new Date());
 
        File file = new File("");
        String rootPath = file.getAbsolutePath().split("src")[0];
        String savePath = rootPath + separ + "profileImage" + separ + today;
        if (!new File(savePath).exists()) {
            try {
                new File(savePath).mkdirs();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
 
        int len = multipartFileList.size();
        for (int i = 0; i < len; i++) {
            // multipartFileList와 userList의 길이가 무조건 같고, 순서가 일치하다고 가정한다.
            MultipartFile multipartFile = multipartFileList.get(i);
 
            String originFileName = multipartFile.getOriginalFilename();
            String saveFileName = UUID.randomUUID().toString() + originFileName.substring(originFileName.lastIndexOf("."));
 
            String filePath = savePath + separ + saveFileName;
            multipartFile.transferTo(new File(filePath));
 
            userList.get(i).setProfileImageUrl(filePath);
        }
 
        return userRepository.saveAll(userList).stream().map(user -> UserGetRes.of(user)).collect(Collectors.toList());
    }
    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
cs

받은 요청을 처리할 Service입니다.

File과 User List의 길이와 순서가 같다고 가정하고 구현한 코드입니다.

중요한건 "File과 User의 List를 같이 받는 것" 입니다.

파일을 지정한 경로에 저장한 후 DB에 추가하는 로직이며, 이전 글에 List만 추가되었습니다.

 

우선 단일 File, Dto 요청 시에는 Body -> form-data에서 File 업로드 및 Dto는 json 방식으로 작성하면 된다고 하였습니다.

 

 

아 맞다 contentType 추가해야하는데

 

Dto 쪽에는 contentType을 application/json으로 맞춰줘야 합니다.

 

 

List를 추가한다고 해서 크게 달라질건 없었습니다.

File은 같은 value로 원하는 수만큼 추가하면 되고,

Dto는 똑같이 json 방식으로 작성해주고, List가 되었으니 대괄호 "[]" 만 추가하면 되겠습니다. (중괄호 "{}" 아닙니다!)

 

이제 Send를 누르면 데이터가 잘 들어가는 것을 확인할 수 있습니다.

 

확인해야할 부분은

Postman에서 입력할 KEY와 @RequestPart의 value로 지정한 값이 일치해야 한다는 것

같은 KEY로 파일을 등록하면 List가 된다는 것

Dto는 form-data에서 json 방식으로 등록해야 하며, List는 대괄호를 추가해야 한다는 것

Dto의 contentType을 application/json으로 맞춰야 한다는 것

정도가 되겠습니다.

 

알고리즘

1일 1알고리즘

 

영어 회화 스터디

꾸준히 단어암기 및 회화 진행중이다.

퇴근하고 나면.. 쉬고싶어져서 쉽지않다. 내 의지 문제인듯

 

후기

확실히 흥미를 가지는 알고리즘은 바쁘든 말든 꾸준히 하는 반면에 영어는 확실한 의지를 가져야 할것 같다.

스터디원도 그런 어려움을 겪어서인지 이번주는 많은 공부를 하지 못했다.

다음주에는 더 열심히 하는 한 주가 되었으면 좋겠다.

'잡담' 카테고리의 다른 글

10월 2주차 결산  (0) 2022.10.16
10월 1주차 결산  (0) 2022.10.10
9월 4주차 결산  (0) 2022.09.26
9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (2) 2022.09.11

알고리즘

1일 1알고리즘

 

영어 회화 스터디

이번주에 배정된 단어를 외우고 시험을 보는 식으로 진행 중이고, 회화는 시간 될 때마다 온라인으로 진행 중이다.

친구가 개발에도 관심이 있어 갑자기 단어 시험 웹 페이지를 만들자는 얘기가 나왔고, 프로젝트 진행 중이다.

 

후기

단어가 너무 많다.. 언제 다외우지?

회사에서도 규모가 적지만 개발에 들어갔고, 배울 사람이 있어 도움이 많이 되는것 같다.

내가 못해봤던 것들은 공부부터 해야해서 시간이 좀 걸리지만 뒤쳐지지 않도록 열심히 해야겠다.

'잡담' 카테고리의 다른 글

10월 1주차 결산  (0) 2022.10.10
9월 5주차 결산  (0) 2022.10.02
9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (2) 2022.09.11
9월 1주차 결산  (0) 2022.09.05

알고리즘

1일 1알고리즘

 

영어 회화 스터디

회화는 하고있지만 단어를 모르니 막히는 부분이 너무 많다.

단어 책도 구매했으니 꾸준히 해야겠다.

 

후기

오랜만에 자바스크립트를 봤더니 원래 모르는게 많았지만 까먹은 것도 많았다.

진짜 공부란 끝이 없는 것 같다..

다음주도 화이팅!

'잡담' 카테고리의 다른 글

9월 5주차 결산  (0) 2022.10.02
9월 4주차 결산  (0) 2022.09.26
9월 2주차 결산  (2) 2022.09.11
9월 1주차 결산  (0) 2022.09.05
8월 4주차 결산  (0) 2022.08.29

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

 

18112번: 이진수 게임

첫 번째 줄에 길이 L의 ‘시작 이진수’가 주어진다. 두 번째 줄에 길이 K의 ‘목표 이진수’가 주어진다. (1 ≤ L, K ≤ 10)

www.acmicpc.net

  1. 맨 앞 숫자를 제외한 한 자리 숫자를 보수로 바꾸기
  2. 현재 수에 1 더하기
  3. 현재 수에 1 빼기 (현재 수가 자연수일 때만 해당)

위 과정을 반복하여 시작 이진수 s가 목표 이진수 e가 되는 최소 동작 횟수를 출력하는 문제로 오랜만에 bfs입니다.

입력은 10자리 이진수로 주어지며, 저는 이 수를 10진수로 변환하여 비트마스킹을 이용하였습니다.

 

이 풀이의 로직은

먼저, 이진수를 10진수로 변환하고, s를 시작으로 bfs 탐색합니다.

 

그 다음은 x가 0인 경우를 제외하고 한 비트씩 변경된 값을 queue에 넣습니다.

이 과정에서 bit가 x / 2보다 커진다면 마지막 비트이므로 break를 걸어줍니다.

 

e가 10자리 이진수이므로 1024보다 작은 수입니다.

따라서 x + 1이 1024보다 작은 경우에만 queue에 넣어주면 됩니다.

 

그리고 e는 음수가 아니므로 x가 자연수일 경우에만 x - 1을 queue에 넣어줍니다.

 

위의 과정을 반복하여 e에 도착하면 t를 리턴하여 출력합니다.

 

 

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
#include <iostream>
#include <queue>
#include <string>
#define MAX 10
using namespace std;
 
string str1, str2;
bool visit[1 << MAX];
int s, e;
 
int bfs() {
    queue<int> q;
    q.push(s);
    visit[s] = 1;
    for (int t = 0!q.empty(); t++) {
        int qsize = q.size();
        while (qsize--) {
            int x = q.front();
            q.pop();
 
            if (x == e) {
                return t;
            }
 
            int bit = 1;
            while (1) {
                if (!x) break;
                if (x & bit) {
                    if (bit > x / 2break;
                }
 
                int next = x ^ bit;
                if (!visit[next]) {
                    q.push(next);
                    visit[next] = true;
                }
                bit <<= 1;
            }
 
            if (x + 1 < 1024 && !visit[x + 1]) {
                q.push(x + 1);
                visit[x + 1= true;
            }
            if (x && !visit[x - 1]) {
                q.push(x - 1);
                visit[x - 1= true;
            }
        }
    }
 
    return 0;
}
 
void func() {
    cout << bfs() << '\n';
}
 
void init() {
    int len1 = str1.size();
    int mul = 1;
    for (int i = len1 - 1; i >= 0; i--) {
        if (str1[i] == '1') s += mul;
        mul <<= 1;
    }
 
    int len2 = str2.size();
    mul = 1;
    for (int i = len2 - 1; i >= 0; i--) {
        if (str2[i] == '1') e += mul;
        mul <<= 1;
    }
}
 
void input() {
    cin >> str1 >> str2;
    init();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > bfs' 카테고리의 다른 글

boj 16985 Maaaaaaaaaze  (0) 2022.12.16
boj 14466 소가 길을 건너간 이유 6  (1) 2022.10.13
boj 16946 벽 부수고 이동하기 4  (0) 2022.05.22
boj 5547 일루미네이션  (0) 2022.05.15
boj 16932 모양 만들기  (0) 2022.05.14

알고리즘

1일 1알고리즘

 

영어 회화 스터디

회사 생활 하면서 가장 느꼈던 것이 영어다.

영어를 안한지 너무 오래돼서 무슨 말인지 모르겠더라 ㅎㅎ

그래서 같이 영어공부할 친구를 구해서 스터디를 시작했다.

물론 어떻게 공부해야 할 지 몰라서 추천받은 방식으로 일단 진행 해보려고 한다.

 

후기

사실 영어를 좀더 일찍 했어야 했는데 내 의지 문제였던걸로 ㅎㅎ..

지금이라도 시작했고, 단어나 문법을 몰라도 회화를 시도해보려고 한다.

얼마나 걸릴진 모르겠다.

이번주 추석이라 본가에 다녀왔다.

취업을 하고 처음 방문이라 좀더 새로웠던 것 같다.

월급도 받았다보니 부모님께 용돈을 처음으로 드렸고, 좋아하시는 모습을 보고 뭔가 뿌듯했다.

내일은 쉬려고 빨리 올라왔지만 자주 내려가야겠다.

'잡담' 카테고리의 다른 글

9월 4주차 결산  (0) 2022.09.26
9월 3주차 결산  (0) 2022.09.18
9월 1주차 결산  (0) 2022.09.05
8월 4주차 결산  (0) 2022.08.29
8월 3주차 결산  (0) 2022.08.22

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

 

2912번: 백설공주와 난쟁이

첫째 줄에 난쟁이의 수 N과 모자 색상의 수 C가 주어진다. (3 ≤ N ≤ 300,000, 1 ≤ C ≤ 10,000) 둘째 줄에는 각 난쟁이가 쓰고 있는 모자의 색상이 줄을 서 있는 순서대로 주어진다. 셋째 줄에는 사진

www.acmicpc.net

N명의 난쟁이가 모자를 쓰고 줄을 서고 있습니다.

구간 l ~ r에 존재하는 모자의 색상의 갯수를 종류별로 파악하여 어떤 모자가 절반보다 많이 있다면 yes와 색의 번호를 출력합니다.

 

이 문제에 업데이트가 없으므로 오프라인 쿼리가 가능하며, 구간을 효율적으로 움직이기 위해 Mo's 알고리즘을 사용합니다.

먼저 구간을 sqrt(N)으로 나누어 l / sqrt(N)을 기준으로 오름차순 정렬합니다.

l의 구간이 같다면 r을 기준으로 오름차순 정렬합니다.

 

그 다음 정렬된 쿼리를 하나씩 처리합니다.

구간을 이동하면서 색에 대한 카운팅을 진행합니다.

해당 구간에 대한 카운팅이 끝났으면 어떤 색이 절반보다 많이 차지하는지 찾습니다. (없다면 -1입니다.)

그 다음 구간을 갱신합니다.

M개의 쿼리를 모두 처리한 다음 한꺼번에 출력합니다.

 

 

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 <algorithm>
#include <cmath>
#define MAX 300001
#define MAXQ 10001
using namespace std;
 
typedef struct Point {
    int l, r, idx;
}Point;
 
Point q[MAXQ];
int list[MAX], ans[MAXQ], cnt[MAXQ];
int N, C, M, sqrtN;
 
bool cmp(Point a, Point b) {
    int x = a.l / sqrtN;
    int y = b.l / sqrtN;
 
    if (x == y) return a.r < b.r;
    else return x < y;
}
 
void solve(int idx, int k) {
    for (int i = 1; i <= C; i++) {
        if (cnt[i] > k / 2) {
            ans[idx] = i;
            return;
        }
    }
    ans[idx] = -1;
}
 
void func() {
    int l = q[1].l;
    int r = q[1].r;
    for (int i = l; i <= r; i++) {
        cnt[list[i]]++;
    }
    solve(q[1].idx, r - l + 1);
 
    for (int i = 2; i <= M; i++) {
        int nl = q[i].l;
        int nr = q[i].r;
        int idx = q[i].idx;
 
        for (int j = l; j < nl; j++) {
            cnt[list[j]]--;
        }
        for (int j = l - 1; j >= nl; j--) {
            cnt[list[j]]++;
        }
        for (int j = r; j > nr; j--) {
            cnt[list[j]]--;
        }
        for (int j = r + 1; j <= nr; j++) {
            cnt[list[j]]++;
        }
        solve(idx, nr - nl + 1);
 
        l = nl;
        r = nr;
    }
 
    for (int i = 1; i <= M; i++) {
        if (ans[i] == -1cout << "no\n";
        else cout << "yes " << ans[i] << '\n';
    }
}
 
void input() {
    cin >> N >> C;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
    sqrtN = sqrt(N);
 
    cin >> M;
    for (int i = 1; i <= M; i++) {
        cin >> q[i].l >> q[i].r;
        q[i].idx = i;
    }
    sort(q + 1, q + 1 + M, cmp);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > Mo's' 카테고리의 다른 글

boj 12999 화려한 마을3  (0) 2022.09.07

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

 

12999번: 화려한 마을3

첫 번째 줄에 N, Q (1 ≤ N ≤ 100,000, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 수와 민호가 궁금해 하는 특정 구간의 수이다. 두 번째 줄에는 1번 집부터 N번 집까

www.acmicpc.net

구간 l ~ r에 존재하는 집에 칠해진 페인트의 밝기들 중 가장 많은 것의 갯수를 출력합니다.

즉, [l, r] 구간에 {1, 1, 1, 2, 2}가 있다면 1이 3개, 2가 2개이므로 3을 출력합니다.

 

이 문제에서 사용할 배열은

  • q: 쿼리 정보를 저장하는 배열
  • list: 페인트의 밝기를 저장하는 배열
  • ans: 답을 저장하는 배열
  • cnt: 특정 밝기의 등장 횟수
  • cntCnt: 특정 밝기의 등장 횟수의 등장 횟수

여기서 cntCnt는 1이 3번, 2가 3번 등장했다면 cntCnt[3] = 2 이렇게 되는 방식입니다.

페인트의 밝기는 -100000 ~ 100000이므로 100000을 미리 더해주도록 합시다. 그래서 cnt 배열의 크기는 *2가 됩니다.

 

페인트의 밝기는 업데이트가 되지 않으므로 오프라인 쿼리 사용이 가능합니다.

따라서 쿼리 정보를 한 번에 받아서 배열 q에 저장합니다.

출력은 입력 순으로 해야하니 인덱스도 함께 저장해야 합니다.

 

그 다음, 투 포인터 비슷하게 l ~ r 구간을 하나하나 확인하여 갯수 카운팅을 진행합니다.

하지만 범위가 1 ~ 5 -> 9 ~ 10 -> 1 ~ 2 이런식으로 된다면 포인터가 이동하는데 많은 시간이 걸릴 것입니다.

포인터를 효율적으로 이동시키기 위해 Mo's 알고리즘을 사용합니다.

Mo's 알고리즘으로 구간을 sqrt(N)으로 나누어서 최대한 같은 그룹 내에서 이동할 수 있도록 하여 움직임을 최소화할 수 있습니다.

따라서 l / sqrt(N)을 기준으로 오름차순으로 정렬하며, 같다면 r 기준으로 오름차순으로 정렬합니다.

 

이제 정렬된 쿼리를 하나씩 처리하면 됩니다.

구간을 이동시키면서 수를 제거하고, 추가합니다.

 

수를 제거하는 과정에서는 먼저 해당 수가 등장한 횟수인 cntCnt를 하나 빼야합니다.

1이 3번 등장해서 cntCnt[3] = 1이 되었는데, 구간을 이동시키니 1이 제거돼야 하는 상황이라면 1이 2번 등장하는 것으로 바뀌게 됩니다.

따라서 cntCnt[3]에서 1을 빼고, cntCnt[2]에서 1을 더해야 합니다.

이 때, 만약 cntCnt[3]이 0이 되었고, 그게 현재 최대 등장 횟수 ret이었다면 ret 또한 1을 빼야합니다.

 

수를 추가하는 과정에서는 해당 수가 처음 등장하는 수가 아니라면 cntCnt를 하나 빼줍니다.

1이 3번 등장해서 cntCnt[3] = 1이 되었고, 구간을 이동시켜서 1이 추가되는 상황이라면 1이 4번 등장하는 것으로 바뀌게 됩니다.

따라서 cntCnt[3]에서 1을 빼고, cntCnt[4]에서 1을 더해야 합니다.

물론, 1이 등장한 적 없다가 추가되는 상황이면 cntCnt를 빼지 않아야 합니다.

그리고 등장 횟수를 추가하는 과정이므로 최대 등장 횟수 ret을 계속 갱신합니다.

 

위 두 가지 작업이 완료되었다면 구간을 갱신하고, 쿼리의 인덱스에 맞게 정답을 넣어줍니다.

쿼리가 입력 순으로 정렬되어 있지 않으므로 출력은 마지막에 한꺼번에 진행합니다.

 

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

 

12986번: 화려한 마을2

첫 번째 줄에 N, Q (1 ≤ N ≤ 100,000, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 수와 민호가 궁금해 하는 특정 구간의 수이다. 두 번째 줄에는 1번 집부터 N번 집까

www.acmicpc.net

이 문제와 같은 풀이입니다.

하지만 화려한 마을2 문제는 다른 조건이 있어 다른 풀이로도 풀 수 있습니다.

저는 화려한 마을3을 먼저 풀었기에 같은 풀이로 AC를 받았습니다.

 

 

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
95
96
97
98
99
100
101
#include <iostream>
#include <algorithm>
#include <cmath>
#define MAX 100001
using namespace std;
 
typedef struct Point {
    int l, r, idx;
}Point;
 
Point q[MAX];
int list[MAX], ans[MAX], cnt[MAX * 2], cntCnt[MAX];
int N, M, sqrtN;
 
bool cmp(Point a, Point b) {
    int x = a.l / sqrtN;
    int y = b.l / sqrtN;
    if (x == y) return a.r < b.r;
    else return x < y;
}
 
void func() {
    int l = q[1].l;
    int r = q[1].r;
    int ret = 0;
    for (int i = l; i <= r; i++) {
        int x = list[i] + 100000;
        if (cnt[x]) cntCnt[cnt[x]]--;
        cnt[x]++;
        cntCnt[cnt[x]]++;
        ret = max(ret, cnt[x]);
    }
    ans[q[1].idx] = ret;
 
    for (int i = 2; i <= M; i++) {
        int nl = q[i].l;
        int nr = q[i].r;
        int idx = q[i].idx;
 
        for (int j = l; j < nl; j++) {
            int x = list[j] + 100000;
            cntCnt[cnt[x]]--;
            if (!cntCnt[cnt[x]] && ret == cnt[x]) ret--;
            cnt[x]--;
            cntCnt[cnt[x]]++;
        }
        for (int j = l - 1; j >= nl; j--) {
            int x = list[j] + 100000;
            if (cnt[x]) cntCnt[cnt[x]]--;
            cnt[x]++;
            cntCnt[cnt[x]]++;
            ret = max(ret, cnt[x]);
        }
        for (int j = r; j > nr; j--) {
            int x = list[j] + 100000;
            cntCnt[cnt[x]]--;
            if (!cntCnt[cnt[x]] && ret == cnt[x]) ret--;
            cnt[x]--;
            cntCnt[cnt[x]]++;
        }
        for (int j = r + 1; j <= nr; j++) {
            int x = list[j] + 100000;
            if (cnt[x]) cntCnt[cnt[x]]--;
            cnt[x]++;
            cntCnt[cnt[x]]++;
            ret = max(ret, cnt[x]);
        }
 
        l = nl;
        r = nr;
        ans[idx] = ret;
    }
 
    for (int i = 1; i <= M; i++) {
        cout << ans[i] << '\n';
    }
}
 
void input() {
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
    sqrtN = sqrt(N);
 
    for (int i = 1; i <= M; i++) {
        cin >> q[i].l >> q[i].r;
        q[i].idx = i;
    }
    sort(q + 1, q + 1 + M, cmp);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > Mo's' 카테고리의 다른 글

boj 2912 백설공주와 난쟁이  (0) 2022.09.08

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

 

12895번: 화려한 마을

첫 번째 줄에 N, T, Q (1 ≤ N ≤ 100,000, 1 ≤ T ≤ 30, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 개수, 사용할 색의 개수, 작업의 개수를 의미한다. 두 번째 줄부터 작

www.acmicpc.net

출력은 l ~ r 구간에 칠해져 있는 색의 가짓수, 집에 칠할 수 있는 색은 최대 30가지입니다.

업데이트와 쿼리가 섞여 있으므로 오프라인 쿼리 및 mo's 알고리즘은 사용하지 못하고, 세그먼트 트리 lazy를 사용합니다.

트리에는 해당 구간에 칠해져 있는 색의 가짓수를 비트 필드로 저장합니다.

 

우선 모든 집에 1이 칠해져 있는 상태로 시작합니다. init 함수에서 초기화를 진행합니다.

type == C면 l ~ r 구간에 업데이트를 진행합니다.

원래 칠해져 있던 색을 지우고, 새로운 색을 채워야 하므로 tree[node]에 해당 색으로 변경해줍니다.

type == Q면 l ~ r 구간에 칠해져 있는 색의 가짓수를 출력합니다.

트리에는 비트 필드를 저장했으니 시프트연산을 하면서 1의 갯수를 출력합니다.

lazyUpdate는 모든 update, query 시 먼저 진행합니다.

 

이 문제는 l > r인 입력도 주어지니 l < r이 되도록 swap을 해야합니다.

 

 

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
#include <iostream>
#define MAX 100001
using namespace std;
 
int tree[MAX * 4], lazy[MAX * 4];
int N, M, K;
 
void init(int node, int s, int e) {
    if (s == e) {
        tree[node] = 1;
        return;
    }
 
    int m = (s + e) / 2;
    init(node * 2, s, m);
    init(node * 2 + 1, m + 1, e);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
void lazyUpdate(int node, int s, int e) {
    if (!lazy[node]) return;
 
    tree[node] = lazy[node];
    if (s != e) {
        lazy[node * 2= lazy[node];
        lazy[node * 2 + 1= lazy[node];
    }
    lazy[node] = 0;
}
 
void update(int node, int s, int e, int l, int r, int k) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return;
    if (l <= s && e <= r) {
        lazy[node] = (1 << k);
        lazyUpdate(node, s, e);
        return;
    }
 
    int m = (s + e) / 2;
    update(node * 2, s, m, l, r, k);
    update(node * 2 + 1, m + 1, e, l, r, k);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
int query(int node, int s, int e, int l, int r) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return 0;
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    return query(node * 2, s, m, l, r) | query(node * 2 + 1, m + 1, e, l, r);
}
 
void func() {
    char type;
    int l, r, k;
    while (K--) {
        cin >> type;
        if (type == 'C') {
            cin >> l >> r >> k;
            if (l > r) swap(l, r);
            update(11, N, l, r, k - 1);
        }
        else {
            cin >> l >> r;
            if (l > r) swap(l, r);
            int ret = query(11, N, l, r);
            int ans = 0;
            while (ret) {
                ans += (ret & 1);
                ret >>= 1;
            }
            cout << ans << '\n';
        }
    }
}
 
void input() {
    cin >> N >> M >> K;
    init(11, N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 10167 금광  (0) 2022.09.02
boj 16993 연속합과 쿼리  (2) 2022.08.29
boj 12846 무서운 아르바이트  (0) 2022.08.19
boj 1849 순열  (0) 2021.09.08

알고리즘

1일 1알고리즘

 

후기

오랜만에 ssafy 5기 1학기 같은반 교육생분들을 만났다.

한 20명? 정도 모였던것 같은데 처음보는 분들이 대부분이어서 신기했다.

사실 1학기때는 민폐만 끼친 것 같고, 좋은 기억이 없었어서 가는게 맞는지 고민을 많이 했었다.

하지만 단체로 모여서 얘기도 많이 나누고 하니 좋은 시간이었던 것 같고, 수업을 오프라인으로 했더라면.. 재미있었겠다 하는 생각도 들었다 ㅠ

다들 어디서 많이 들어본 좋은 곳으로 취업하신걸 보니 대단했다. 물론 나처럼 스타트업에서 역량 키우시는 분도 계셔서 반가웠다. ㅎㅎ

 

역시나 개발자들이 모이니 개발 관련 얘기를 많이 했고, 이번 모임에서 느낀것은 정말 열심히 사는분들이 많다는 것이다.

취업했다고 끝이 아닌, 성장 혹은 경험을 위해 어떤 프로그램에 참여하거나 스터디도 꾸준히 하는 분이 많았고, 자격증을 따시는 분도 많았다.

덕분에 내가 느낄 수 있는 점도 많고, 내 자신을 다시 반성하게 되면서 ㅎㅎㅎ 더 열심히 살아야 겠다는 생각이 든다.

앞으로 이렇게 만날 기회가 거의 없을 것 같은데 이번에라도 만나서 다행이라는 생각이 들었다.

'잡담' 카테고리의 다른 글

9월 3주차 결산  (0) 2022.09.18
9월 2주차 결산  (2) 2022.09.11
8월 4주차 결산  (0) 2022.08.29
8월 3주차 결산  (0) 2022.08.22
8월 2주차 결산  (0) 2022.08.14

+ Recent posts