Skip to content

Commit

Permalink
Merge pull request #13 from Altu-Bitu/1012_extra
Browse files Browse the repository at this point in the history
[투포인터] 10월 12일 -update
  • Loading branch information
spiritstone authored Nov 4, 2021
2 parents 873b361 + bb4662e commit e85e9b0
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 0 deletions.
60 changes: 60 additions & 0 deletions 10월 12일 - 투 포인터/1253_re.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Created by user on 2021-11-02.
//

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

//좋은 수인지 검사하는 함수(투 포인터)
bool isGood(vector<int> &num, int left, int right, int idx) { // n개 수가 저장된 정렬된 벡터, 왼쪽(벡터 시작점), 오른쪽(벡터 끝점), 현재 수 위치
while (left < right) { // 양 끝에서 차례로 계산하기 때문에 left가 right보다 작을 경우만 계산하면 된다.
if (left == idx) { //left가 현재 수 위치와 같은 경우
left++; // left 위치 한칸 증가
continue; // 계속
}
if (right == idx) { //right가 현재 수 위치와 같은 경우
right--; // right 위치 한칸 감소
continue; // 계속
}

if (num[left] + num[right] == num[idx]) // left와 right위치에 있는 수의 합이 현재 수와 같다면
return true; // true 반환

if (num[left] + num[right] > num[idx]) //만드려는 수보다 크다면
right--; // 오른쪽 위치 감소 (이전 수가 무조건 더 작으니까(정렬되어있음))
else // 만드려는 수보다 작다면
left++; // 왼쪽 위치 증가 (다음 수가 무조건 더 크니까)
} // end of while
return false; // 조건 만족하지 않으면 false 리턴
}

/**
* 1. 각 수마다 양 쪽 끝에서 포인터를 시작해서 좁혀오면서 어떤 '다른 두 수'가 현재 수를 만들 수 있는지 검사
* 2. 포인터를 차례로 옮기며 검사하기 위해 미리 수를 오름차순 정렬함
* 3. 현재 만드려는 수의 위치와 left, right 포인터 위치가 겹칠 경우 처리 주의
*/

int main() {
int n, ans = 0;

//입력
cin >> n;
vector<int> num(n, 0); // N개의 수 저장할 벡터 생성
for (int i = 0; i < n; i++) // 반복문 돌아가면서
cin >> num[i]; // n개의 수 입력받기

//연산
sort(num.begin(), num.end()); // 오름차순 정렬
for (int i = 0; i < n; i++) { // n번만큼 반복문 돌면서
if (isGood(num, 0, n - 1, i)) // 좋은 수라면
ans++; // 정답 추가
}

//출력
cout << ans << '\n';

return 0;
}
92 changes: 92 additions & 0 deletions 10월 12일 - 투 포인터/14500_re.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Created by user on 2021-11-02.
//

#include <iostream>
#include <vector>

using namespace std;

int ans, n, m;
vector<vector<int>> board; // 보드판
int dr[4] = {-1, 1, 0, 0}; // 상 하
int dc[4] = {0, 0, -1, 1}; // 좌 우

//board[row][col]을 가운데로 하는 +모양 만들기
int horn(int row, int col) {
int cnt = 0, min_block = 1001, sum = board[row][col]; // 입력으로 최대가 될 수 있는 수: 1000 -> min_block에서는 최솟값 찾기 때문에 최대값으로 먼저 초기화

for (int i = 0; i < 4; i++) { // 상하좌우 방향 탐색
int nr = row + dr[i]; // 상 하로 이동하며 row 좌표 리셋
int nc = col + dc[i]; // 좌 우로 이동하며 col 좌표 리셋

if (nr < 0 || nr >= n || nc < 0 || nc >= m) //범위를 벗어나면 무시
continue; // 코드 진행
min_block = min(min_block, board[nr][nc]); //가장자리 블럭 중 값이 가장 작은 블럭
sum += board[nr][nc]; // 총합은 보드의 범위 내에 있는 좌표의 값 다 더한 것
cnt++; // 반복문 한 번 돌 때마다 cnt 증가
}

if (cnt < 3) //가능한 가장자리가 최소 3개 이상이어야 함
return 0; // 함수 종료
if (cnt == 3) //ㅗㅏㅜㅓ
return sum; // 보라색 조각 만들었으니 최종 전체합임.
//+
return sum - min_block; // 전체 합에서 최솟값 빼면 최대값
}

//한붓 그리기가 가능한 블럭들 백트래킹 탐색
void backtracking(int row, int col, int cnt, int sum) {
if (cnt == 4) { //기저조건 : 4개의 블럭을 탐색함
ans = max(ans, sum); // 최대값 갱신
return; // 종료
}

for (int i = 0; i < 4; i++) { // 상하좌우 방향 탐색
int nr = row + dr[i]; // row 좌표 갱신
int nc = col + dc[i]; // col 좌표 갱신

if (nr < 0 || nr >= n || nc < 0 || nc >= m || !board[nr][nc]) //범위를 벗어나거나, 이미 방문했다면
continue; // 무시
int save = board[nr][nc]; // 현재 블럭의 값 저장
board[nr][nc] = 0; // 방문했으니까 0으로 리셋
backtracking(nr, nc, cnt + 1, sum + save); // 갱신된 데이터로 재귀
board[nr][nc] = save; // backtracking 함수 구현 후 다음 탐색을 위해 0이었던 값에서 저장해 둔 원래 블럭의 값 돌려두기
}
}

/**
* 1. 각 블럭을 백트래킹이 가능한 블럭과 불가능한 블럭으로 나누기
* -> 블럭을 한붓 그리기로 그릴 수 있으면 백트래킹이 가능 아니라면 불가능
* -> 보라색 블럭을 제외하면 모두 백트래킹 가능
* 2. 보라색 블럭은 + 모양에서 가장자리를 하나 제거한 것과 같음
* -> 가운데 블럭을 중심으로 가장자리 블럭을 붙여보고 가능한 최댓값 구하기
* 3. 각 블럭에 대해 깊이가 4인 백트래킹 함수를 수행하며 최댓값 갱신
*
* 해설 : https://myunji.tistory.com/297
* *코드가 살짝 달라요(블로그 코드는 최적화 하기 전)
*/
int main() {
//입력
cin >> n >> m;
board.assign(n, vector<int>(m, 0)); // 세로 n, 가로 m인 보드 세팅
for (int i = 0; i < n; i++) { // 세로 만큼 반복문
for (int j = 0; j < m; j++) // 가로만큼 반복문 돌면서
cin >> board[i][j]; // 칸에 쓰인 수 입력
}

//연산
for (int i = 0; i < n; i++) { // 세로 크기만큼
for (int j = 0; j < m; j++) { // 가로 크기만큼 반복하면서
ans = max(ans, horn(i, j)); //보라색 블럭 처리
int save = board[i][j]; // 원래 블럭의 값 save에 저장

board[i][j] = 0; // 방문했으니 값 0으로 만들어준다
backtracking(i, j, 1, save); //나머지 모양 처리
board[i][j] = save; // backtracking 끝났으면 다시 원래 블럭의 값 저장
}
}

//출력
cout << ans;
}
63 changes: 63 additions & 0 deletions 10월 12일 - 투 포인터/2531_re.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Created by user on 2021-11-02.
//

#include <iostream>
#include <vector>

using namespace std;

int chooseSushi(vector<int> &belt, int n, int d, int k, int c) {
vector<int> sushi(d + 1, 0); // 초밥 가짓수보다 하나 더 많은 크기의 벡터 생성

//쿠폰으로 먹은 초밥
int section_sushi = 1; // 쿠폰으로 먹은 초밥 우선 더해두기
sushi[c]++; // 쿠폰으로 먹은 초밥 체크

//윈도우 초기화
for (int i = 0; i < k; i++) { // 연속해서 먹는 접시의 수만큼 반복하면서
sushi[belt[i]]++; // 나오는 스시들 체크
if (sushi[belt[i]] == 1) // 한 번 먹었다면 (전에 안나온 거니까)
section_sushi++; // 그 종류로 먹는건 처음이니 증가
}

int ans = section_sushi; //
int left = 0, right = k - 1; // 윈도우 양 끝 초기값 설정
do { //원형태로 윈도우를 옮겨야 하기 때문에 종료조건은 left가 초기값(=0)이 됐을 때
sushi[belt[left]]--; // 윈도우 시작에 있는 초밥 종류를 먹은 값 감소했는데
if (!sushi[belt[left]]) // 0이면 윈도우에 이 초밥 종류가 없다는 의미니까
section_sushi--; // 먹은 종류감소

//윈도우의 양 끝 이동
left = (left + 1) % n; // n 넘어갈 수 있을 수 있으니 모듈러 연산
right = (right + 1) % n;

sushi[belt[right]]++; // 윈도우 끝에 있는 초밥을 먹은 횟수를 증가했는데
if (sushi[belt[right]] == 1) // 새로 먹은 초밥 종류라면
section_sushi++; // 먹은 종류 증가

ans = max(ans, section_sushi); // 최대값으로 갱신
} while (left); // left가 0이면 종료
return ans;
}

/**
* 1. 연속해서 먹어야 할 접시가 k로 고정됐기 때문에 슬라이딩 윈도우
* 2. 직선이 아니라 원 형태의 배열! 모듈러 연산을 통해 윈도우의 양 끝 위치 조절하기
* 3. 쿠폰으로 먹은 초밥을 먼저 고려해놓기
* 4. 초밥의 종류가 최대 3000개로 많지 않음. 보석 쇼핑 문제처럼 각 종류별 먹은 초밥의 개수를 세어주기
*
* 그림 참고: https://cocoon1787.tistory.com/280
*/
int main() {
int n, d, k, c; // n: 벨트에 놓인 접시 수, d: 초밥의 가짓수, k: 연속해서 먹는 접시의 수, c: 쿠폰 번호

//입력
cin >> n >> d >> k >> c;
vector<int> belt(n, 0); // 길이가 n인 벡터 생성
for (int i = 0; i < n; i++) // n번만큼 반복하면서
cin >> belt[i]; // 각 벨트 위치의 회전초밥의 종류를 나타내는 정수 입력

//연산 & 출력
cout << chooseSushi(belt, n, d, k, c);
}
53 changes: 53 additions & 0 deletions 10월 12일 - 투 포인터/2607_re.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Created by user on 2021-11-02.
//

#include <iostream>
#include <vector>

using namespace std;
const int SIZE = 26; // 알파벳 수 26개

//알파벳별 등장횟수 저장
vector<int> alphaMap(string str) {
vector<int> result(SIZE, 0); // 알파벳 수 만큼의 크기의 벡터 생성
for (int i = 0; i < str.size(); i++) // 입력한 문자열의 길이만큼 반복문 돌면서
result[str[i] - 'A']++; // 알파벳 등장했다면 배열 수 추가해주기
return result; // 정답 반환
}

/**
* 단어가 같은 구성일 조건
* 1. 두 개의 단어가 같은 종류의 문자로 이루어짐
* 2. 같은 문자는 같은 개수만큼 있음
*
* 비슷한 단어의 조건
* 1. 한 단어에서 한 문자를 더하거나, 빼면 같은 구성이 됨
* -> 두 단어에서 다른 문자의 개수가 총 1개
* 2. 한 단어에서 한 문자를 바꾸면 같은 구성이 됨
* -> 두 단어에서 다른 문자의 개수가 총 2개
* -> !주의! 이때, 두 단어의 길이가 같아야 함 cf) doll | do
*/
int main() {
int n, ans = 0; // n: 단어의 개수
string source, target; // source: 첫번째 단어 (비교군)

//입력
cin >> n >> source;

//연산
vector<int> source_alpha = alphaMap(source); // 첫번째 단어 알파벳 등장횟수 정리
while (--n) { // 첫 단어는 비교군으로 빠졌으니까 --n으로 (n - 1만큼 반복)
cin >> target; // 비교할 단어 입력

int diff = 0; // 두 단어의 차이 계산
vector<int> target_alpha = alphaMap(target); // 비교할 단어 알파벳 등장횟수
for (int i = 0; i < SIZE; i++) //두 단어의 차이
diff += abs(source_alpha[i] - target_alpha[i]); // 알파벳별 등장횟수의 차의 합
if (diff <= 1 || (diff == 2 && source.size() == target.size())) //문자를 더하거나, 빼거나, 바꾸거나
ans++; // 정답 증가
}

//출력
cout << ans;
}

0 comments on commit e85e9b0

Please sign in to comment.