학교에선 Github을 사용하다가 회사를 다니며 Gitlab을 쓰다보니 PR과 MR 단어를 같이 쓰지만 무엇이 다른지 정확히 모르고 사용하고 있었다. 또한 Git-Flow를 공부하다가 이 내용에 대해서 정확히 짚고 나가는 것이 좋다는 생각이 들었다.

 

그래서 이에 대해 알아보니 일단 결론적으론 브랜치를 합치는 작업에서 Github진영에서는 Pull Request라 하고 Gitlab진영에서는 Merge Request라고 부르는 같은 동의어와 같은 것임을 알게 되었다.

 

간단히 설명하자면 Github에서는 내가 작업한 브랜치를 Master의 입장에서 Pull하는 것이기에 Pull Request라 하는 것이고 Gitlab에서는 내가 작업한 브랜치 입장에서 Master에 Merge하는 것이기에 Merge Request라 하는 것이였다.

 

좀 자세히 알아보자면

Pull Request는 첫 액션이 Pull을 하는 것이기에 PR이 된 것이다.

Merge Request는 마지막 액션이 Merge를 하는 것이기에 MR로 부른다.

 

사실상 같은 용어들이므로 자신이 쓰는 서비스에 맞게 용어를 사용하면 될 것 같다!!

 

참고

https://stackoverflow.com/questions/7273434/is-it-possible-to-create-merge-requests-in-pure-git-from-the-command-line?noredirect=1&lq=1

https://prosaist0131.tistory.com/entry/MR%EA%B3%BC-PR%EC%9D%98-%EC%B0%A8%EC%9D%B4

처음에 Git-Flow라는 단어를 들었을 떈 그냥 단순히 브랜치의 흐름? 같은 것이라고 생각을 했었다.

비슷하지만 그런 것이 아니라 Git-Flow는 대표적인 Git 브랜칭 전략이다.

Git-FlowVincent Driessen이라는 분이 처음 개발한 깃 브랜칭 모델이다.

1년여간 이 분이 이러한 브랜칭 모델을 사용했고 그에 대한 이야기를 적어놓았던 글이 화제가 되어 현재 많은 개발자들이 이 모델을 많이 사용하고 있다.

 

Git-Flow에 대해 간략한게 설명해보자면 이 모델은 5가지 브랜치를 사용하는 모델이다.

 

Master(Main) - 제품으로 출시되는 Branch, Production의 개념을 내포하고 있다.

Develop - 다음 출시 버전을 개발하는 Branch

Feature - 기능을 개발하는 Branch, 주로 이 브랜치에서 개발을 한다.

Release - 이번 출시 버전을 준비하는 Branch, QA를 진행하는 브랜치이다.

Hotfix - 출시 버전에서 발생한 버그를 수정하는 Branch

 

 

Git-Flow를 사용해서 개발하는 순서는 보통 아래와 같이 이루어진다.

  • 처음에 Master(Main)와 Develop 생성
  • 새로운 추가 작업은 Develop에서 Feature Branch 생성
  • Feature는 Develop으로 Merge(이때 Develop이 최신 상태인지 확인해야함)
  • QA를 위해서 Develop에서 Release Branch 생성
  • QA에서 발생한 버그는 Release에서 수정
  • QA가 끝나면 Release에서 Develop / Master(Main)으로 각각 Merge
  • Hotfix는 Master에서 시작하여 수정 후 Master / Develop에 Merge

 

Git-Flow가 유용하긴 하지만 너무 많은 브랜치를 사용하는 점, 그에 따라 안쓰는 브랜치가 생기고 브랜치 마다 포지션이 애매해질 수 있기 때문에 이러한 점을 보완해 좀 더 간소화 된 브랜칭 전략이 나왔는데 그것이 GitHub-Flow이다.

 

GitHub-Flow란 Git-Flow가 복잡하기에 이를 좀 더 간소화시킨 방식으로 Scott chacon이 Github에서 좀 더 편리하게 사용하기 위해 만든 브랜칭 전략이다.

자동화 개념이 포함되어 있으며 Master(Main) Brnach에 대한 role만 정확하다면 나머지 Branch들에는 관여를 하지 않는다. 그리고 Pull Request 기능의 사용을 권장한다.

 

사용법 및 특징을 정리해보면

1. Master Branch의 어느것이든 배포가 가능하다.

  • master 브랜치는 항상 최신 상태며, stable 상태로 product에 배포되는 브랜치
  • 이 브랜치에 대해서는 엄격한 role과 함께 사용한다
  • merge하기 전에 충분히 테스트를 해야한다. 테스트는 로컬에서 하는 것이 아니라 브랜치를 push 하고 Jenkins로 테스트 한다

2. 새로운 일을 시작하기 위해 Branch를 Master에서 딴다면 이름은 어떤 작업인지 명확하게 명시한다.

  • 브랜치는 항상 master 브랜치에서 만든다
  • Git-flow와는 다르게 feature 브랜치나 develop 브랜치가 존재하지 않음
  • 새로운 기능을 추가하거나, 버그를 해결하기 위한 브랜치 이름은 자세하게 어떤 일을 하고 있는지에 대해서 작성해주도록 하자
  • 커밋메시지를 명확하게 작성하자

3. Local Branch에 수시로 커밋하고 이 내용을 원격 Branch에 수시로 Push한다.

  • Git-flow와 상반되는 방식
  • 항상 원격지에 자신이 하고 있는 일들을 올려 다른 사람들도 확인할 수 있도록 해준다
  • 이는 하드웨어에 문제가 발생해 작업하던 부분이 없어지더라도, 원격지에 있는 소스를 받아서 작업할 수 있도록 해준다

4. 피드백이나 도움이 필요할 때 혹은 자신의 Branch가 merge할 준비가 되었다면, PR을 생성하여 공유한다.

  • pull request는 코드 리뷰를 도와주는 시스템
  • 이것을 이용해 자신의 코드를 공유하고, 리뷰받자
  • merge 준비가 완료되었다면 master 브랜치로 반영을 요구하자

5. PR 리뷰 후에는 다른 사람의 동의를 얻고 Master에 Merge한다.

  • 곧장 product로 반영이될 기능이므로, 이해관계가 연결된 사람들과 충분한 논의 이후 반영하도록 한다
  • 물론 CI도 통과해야한다!

6. Master Branch로 Merge나 Push가 이루어지면 즉시 배포해야한다.

  • GitHub-flow의 핵심
  • master로 merge가 일어나면 자동으로 배포가 되도록 설정해놓는다

 

마지막으로 GitLab-Flow란 GitLab에서 Git-Flow를 간소화하여 만든 브랜칭 전략이다.

또한 Github-flow는 너무나도 간단하여 배포, 환경 구성, 릴리즈, 통합에 대한 이슈를 남겨둔 것이 많았다.

그것을 보완하기 위해 GitLab에서 관련 내용들을 추가적인 가이드를 제공하여 전략을 만들었다.

아래는 GitLab-Flow의 특징이다.

Production branch with GitLab flow

Github-Flow에서는 매번 Merge후에 배포를 하라고 하지만 iOS앱 등 배포 시기를 맞추지 못하는 서비스 등 이 특징을 매번 적용하기 힘든 경우가 있다.

이러한 경우들을 해결하기 위해 production 브런치가 존재하여 커밋한 내용들을 일방적으로 배포하는 형태를 만들었다. GitHub에서 브런치 하나를 더 구성하여 사용하는 이것도 조금은 간단한 구성이다.
이렇게 구성하면 배포 자동화가 되어있지않은 구성에서 어떻게 배포를 진행할 것인가에 대한 내용을 담았다. 물론 이걸로 부족하여 다음의 것도 추가되었다.

Environment branches with GitLab flow

master와 production 사이에 pre-production을 두어 개발한 내용을 곧장 반영하지 않고 시간을 두고 반영을 하는 것을 말한다. Staging을위한 공간인 pre-production에 MR을 Master에서 보낸다. 이와 같은 형식은 downstream flow의 방식을 취하게 되며 hotfix발생시 master에서 feature 브랜치를 만들어 수정 후 Master에 MR을 보낸다. feature 브랜치를 바로 삭제하지 않고 Master에서의 자동 테스트를 통과를 확인하고 삭제한다.

Release branches with GitLab flow

release한 브런치를 두고서 보안상 문제가 발생한 것이나 백 포트를 위해서 작업을 할 경우, cherry-pick을 이용해서 작업을 진행할 수 도 있다.(2-3 stable이나 2-4 stable등의 브랜치를 만들어서 작업) 아니면 해당 릴리즈에서 발생하는 버그들을 묶어서 수정하는 방식으로 작업한다. 일반적으로 말하는 ‘upstream first’ 정책이다. 같은 버그를 수정하는 경우를 방지하기 위해 태그를 달아서 패치 버전을 관리해주는 것이 좋다.

Merge/pull requests with GitLab flow

Pull request를 사용하는 방법이다. GitHub Flow에서 하는 방법과 동일하다.(Github에서는 pull request이고 GitLab에서는 Merge request임)

보호되고 있는 Long-live branch는 maintainer만 merge할 수 있도록 해야한다. Merge후에는 feature 브랜치는 삭제해주도록 한다.

Issues with GitLab flow

Issue 트러커와 연결하여 사용하는 것을 말한다. 긴 시간 동안 작업을 할 경우, 이슈를 생성하여 작업을 진행하는 것으로 브랜치 이름에는 이슈번호를 적어 작업 중인 이슈가 어떤 것인지를 명확하게 해주는 것이 필요하다.
작업이 끝나거나 코드 공유가 필요한 시점이면 Merge/pull requsts를 보낸다.

 

 

이 글을 작성하면서 가장 많은 도움을 받았던 블로그이다. 특히 GitLab 관련해서 일어 번역을 해주셔서 편하게 공부할 수 있었다.. 또한 장단점 정리가 깔끔했다.

https://ujuc.github.io/2015/12/16/git-flow-github-flow-gitlab-flow/

 

Git flow, GitHub flow, GitLab flow

Git flow, GitHub flow, GitLab flow 에대해서 좀 알아보자. 머리아프다.

ujuc.github.io

 

위의 브랜칭 전략들을 공부하면서 정말 정답은 없다고 느꼈다. 우아한 형제들 블로그에서는 Github-flow를 사용하다가 오히려 Git-flow로 넘어가는 등 자신이 하고 있는 프로젝트나 팀 상황에 맞게 브랜칭 전략을 잘 수립해야 될 것 같다.

 

아래의 공식 사이트 등을 통해 좀 더 자세히 공부할 수 있다~

참고

https://docs.gitlab.com/ee/topics/gitlab_flow.html

https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/

https://guides.github.com/introduction/flow/

https://hellowoori.tistory.com/56

https://ujuc.github.io/2015/12/16/git-flow-github-flow-gitlab-flow/

https://scottchacon.com/2011/08/31/github-flow.html

https://nvie.com/posts/a-successful-git-branching-model/

https://techblog.woowahan.com/2553/

https://muscardinus.tistory.com/223

'개인 공부 > TIL' 카테고리의 다른 글

[Git]PR(Pull Request)과 MR(Merge Request)의 차이는??  (0) 2021.08.23

항상 자바를 공부하다 보면 항상 헷갈리는 부분이 ==과 eqauls의 차이에 대한 것이다.

 

매번 찾아보지만 또 까먹은 김에 아예 글을 작성하게 되었다.

 

간단하게 결론부터 이야기 하자면 ==의 경우 주소값(Call by Reference)까지 비교하는 연산자이고 equals의 경우 값(Call by Value)만 비교하는 메소드이다.

 

예를 들어

String str1 = "abc";
Stirng str2 = new String("abc");

이 두개의 값을 비교해보자

str1의 경우 리터럴을 사용하여 선언하였기에 string constant pool이라는 영역에 존재하게 되고

str2의 경우 new를 통해 String을 생성했기에 Heap 영역에 존재하게 됩니다.

따라서 당연히 두 값의 주소값은 달라질 것이기에 아래와 같은 결과가 나오게 됩니다.

str1 == str2;		// false!!
str1.equals(str2);	// true!!

 

따라서 평상시 자바에서 String을 비교할때에는 equals를 사용해야한다!!

 

참고

https://coding-factory.tistory.com/536

 

[Java] 문자열 비교하기 == , equals() 의 차이점

자바에서 일반적인 데이터 타입의 비교는 ==이라는 연산자를 사용하여 비교합니다. 하지만 String 문자열의 값을 비교할때에는 ==이 아닌 equals()라는 메소드를 사용하여 비교를 합니다. equals와 ==

coding-factory.tistory.com

 

회사에서 일을 하면서 hosts파일을 설정할 일이 생겼다. 정확히 어떤 일을 하는 파일인지 모르고 있었기에 이번 기회에 공부하게 되었다.

 

Hosts란?

호스트 이름에 대응하는 IP주소가 저장되어 있어서 DNS에서 주소를 제공받지 않아도 서버의 위치를 찾게 해주는 파일이다.

 

예를 들어 www.naver.com에 접속을 하게 된다면 외부 DNS서버를 통해 IP가 제공되고 그 IP주소에 우리가 접속을 하게되는 것이다. 만약 호스트 파일에 IP주소와 도메인을 적어놨다면 따로 DNS서버를 거치지 않고 바로 그 IP주소에 접속하게된다.

 

정리하자면

 

▶ 호스트파일의 역할

 - 호스트 이름에 대응하는 IP 주소가 저장되어 있어서 도메인 이름 시스템(DNS)에서 주소 정보를 제공받지 않고도 서버의 위치를 찾게 해준다.

 

 

▶ 호스트 파일 사용 장점

 - 인터넷 속도 향상

 - 리소스 사용을 줄임

 - 보안 문제 예방적 대처

 

 

▶ 호스트 파일 사용 단점

 - 사이트 방문이 차단될 수 있다

 - 페이지 내에서 부분 차단된 경우 디자인, 속도 문제

 

 

▶ 호스트 파일 저장 위치

 C:\windows\system32\drivers\etc\hosts

 

▶ 호스트 파일 작성 원칙

- 샵 기호(#)로 시작하는 줄(line)은 주석문, 개별 줄(line) 앞이나 호스트 이름 다음에 작성

- 각 항목은 한 줄(line)로 작성

- 항목은 IP 주소 + 호스트 이름 순서로 제한

- 호스트 이름과 IP 주소의 간격은 최소한 1칸을 띄움

- 호스트 이름 부분에 'IP 주소' 등록 제한 : 호스트의 IP 주소 검색이 목적. IP 주소를 이미 찾은 상태

- 호스트 이름의 글자수는 255자로 제한

- 프로토콜 형식 'http:', 와일드카드 문자 '*', 주소 맨끝에 사선기호 '/' 사용 제한

 

IP나 도메인 등 좀 더 자세하게 알고 싶다면 아래 내가 참고한 블로그에 들어가서 깊게 공부해보자!

 

참고

https://way-be-developer.tistory.com/86

https://goddaehee.tistory.com/90

'개인 공부 > 네트워크' 카테고리의 다른 글

NAT(Network Address Translation)이란?  (0) 2021.08.20

회사에서 공부를 하다가 NAT이라는 용어가 나와서 들어는 봤지만 헷갈리는 단어라 다시 공부를 하게 되었다.

 

NAT이란 위키에 따르면

네트워크 주소 변환(영어: network address translation, 줄여서 NAT)은 컴퓨터 네트워킹에서 쓰이는 용어로서, IP 패킷 TCP/UDP 포트 숫자와 소스 및 목적지의 IP 주소 등을 재기록하면서 라우터를 통해 네트워크 트래픽을 주고 받는 기술을 말한다. 패킷에 변화가 생기기 때문에 IP나 TCP/UDP의 체크섬(checksum)도 다시 계산되어 재기록해야 한다. NAT를 이용하는 이유는 대개 사설 네트워크에 속한 여러 개의 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위함이다. 많은 네트워크 관리자들이 NAT를 편리한 기법이라고 보고 널리 사용하고 있다. NAT가 호스트 간의 통신에 있어서 복잡성을 증가시킬 수 있으므로 네트워크 성능에 영향을 줄 수 있는 것은 당연하다.

 

라고 한다..

 

말이 좀 어렵게 되어 있지만 간단하게 생각하면 우리가 가정집에서 사용하는 공유기에서 사용하는 여러기기가 있다면 각 기기마다 사설 IP 주소를 만들어주는 것이다. 그리고 우리가 인터넷을 사용할때에는 이러한 개인 IP 주소들을 공인 IP주소로 변환하여 인터넷에 접속을 하게된다.

위의 이미지를 보면 아마 쉽게 이해가 될것이다..

 

우리가 NAT을 사용하는 이유에는 두가지 정도가 있다.

 

첫번째는 공인 IPv4주소를 절약하기 위해서이다.

인터넷상의 공인 IP주소는 유한할 수 밖에 없다. 현재 IPv4의 경우 2의32승의 갯수만을 사용할 수 있으니 이러한 문제를 해결하기 위해서 NAT을 사용해 하나의 공유기에서 여러기기를 연결시키고 이를 통해 공인 IP 주소의 낭비를 줄이는 것이다.

 

두번째는 보안의 목적이다.

공개된 인터넷망에 사설 내부 IP가 노출된다면 개인PC로의 직접적인 접속 등 보안 사고가 발생할 수 있다. 따라서 NAT과 함께 방화벽을 사용함으로써 외부로의 공격을 예방 혹은 방어할 수 있다.

 

NAT의 동작원리나 자세한 사항은 참고자료에 있으니 나중에 공부할때 활용하면 좋을 것 같다!

 

참고

https://www.stevenjlee.net/2020/07/11/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-nat-network-address-translation-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A3%BC%EC%86%8C-%EB%B3%80%ED%99%98/

https://m.blog.naver.com/sehyunfa/221684093830

https://wiki.teltonika-networks.com/view/Network_Address_Translation

https://ko.wikipedia.org/wiki/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EC%A3%BC%EC%86%8C_%EB%B3%80%ED%99%98

'개인 공부 > 네트워크' 카테고리의 다른 글

윈도우 호스트(hosts)파일이란?  (0) 2021.08.20

www.acmicpc.net/problem/17144

 

17144번: 미세먼지 안녕!

미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사

www.acmicpc.net

미세먼지 확산 구현보다 공기청정기 순환을 구현하는 것이 생각보다 까다로웠던 문제이다.

 

미세먼지 확산의 경우 임시적으로 맵을 한개 더 만들고 확산값을 저장한 후 기존의 값에 다시 더해주는 형식으로 구현하였다.

 

공기청정기 바람 순환은 위쪽 4개 + 아래쪽 4개를 각각 포문을 사용하여 구현하였으며 임시값 두개를 사용해 값을 교환해주면서 진행하였으며 각각의 범위를 정하는 것이 까다로워서 시간이 오래걸렸다.

 

공기청정기 구현만 조심하면 어려운 문제는 아니었던 것 같다.

 

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

int R, C, T, ans;
int map1[51][51];
int map2[51][51];
int dx[4] = { 0,1,0,-1 };
int dy[4] = { 1,0,-1,0 };
pair<int, int> ac;

void spread() {
	int tmp = 0;
	int cnt = 0;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			if (map1[i][j] >= 5) {
				tmp = map1[i][j] / 5;
				for (int k = 0; k < 4; k++) {
					int nx = i + dx[k];
					int ny = j + dy[k];
					if (nx >= 0 && ny >= 0 && nx < R 
						&& ny < C && map1[nx][ny] != -1) {
						cnt++;
						map2[nx][ny] += tmp;
					}
				}
				map1[i][j] -= tmp * cnt;
			}
			tmp = 0;
			cnt = 0;
		}
	}
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			map1[i][j] += map2[i][j];
			map2[i][j] = 0;
		}
	}
	return;
}

void rotate() {
	int tmp = 0, tmp2 = 0;
	int x = ac.first;
	tmp = map1[x][1];
	map1[x][1] = 0;
	for (int i = 2; i < C; i++) {
		tmp2 = map1[x][i];
		map1[x][i] = tmp;
		tmp = tmp2;
	}
	for (int i = x-1; i >= 0; i--) {
		tmp2 = map1[i][C - 1];
		map1[i][C - 1] = tmp;
		tmp = tmp2;
	}
	for (int i = C - 2; i >= 0; i--) {
		tmp2 = map1[0][i];
		map1[0][i] = tmp;
		tmp = tmp2;
	}
	for (int i = 1; i < x; i++) {
		tmp2 = map1[i][0];
		map1[i][0] = tmp;
		tmp = tmp2;
	}
	x += 1;
	tmp = 0, tmp2 = 0;
	tmp = map1[x][1];
	map1[x][1] = 0;
	for (int i = 2; i < C; i++) {
		tmp2 = map1[x][i];
		map1[x][i] = tmp;
		tmp = tmp2;
	}
	for (int i = x + 1; i <= R - 1; i++) {
		tmp2 = map1[i][C - 1];
		map1[i][C - 1] = tmp;
		tmp = tmp2;
	}
	for (int i = C - 2; i >= 0; i--) {
		tmp2 = map1[R - 1][i];
		map1[R - 1][i] = tmp;
		tmp = tmp2;
	}
	for (int i = R - 2; i > x; i--) {
		tmp2 = map1[i][0];
		map1[i][0] = tmp;
		tmp = tmp2;
	}
	return;
}

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int check = 0;
	cin >> R >> C >> T;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			cin >> map1[i][j];
			if (map1[i][j] == -1 && check == 0) {
				check = 1;
				ac.first = i;
			}
		}
	}
	while (T--) {
		spread();
		rotate();
	}
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			if (map1[i][j] > 0) {
				ans += map1[i][j];
			}
		}
	}
	cout << ans;
	return 0;
}

www.acmicpc.net/problem/15686

 

15686번: 치킨 배달

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸

www.acmicpc.net

재귀로 풀지 조합으로 풀지 고민하다가 조합으로 푼 문제이다. 다른 사람들 풀이를 보니 재귀로 많이 풀어낸 것 같다.

 

단순히 치킨거리를 구하는 게 아니라 치킨집을 몇개 선정하고 최소 치킨 거리를 구해야 해서 조금 까다로웠다.

 

처음에 모든 치킨 거리의 수를 계산해서 배열에 저장하고 M개의 치킨집은 벡터에 0 나머지는 1을 넣고 그 조합벡터를

next_permutation을 사용해 풀어냈다.

 

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

int N, M, ans = 99999;
int map[51][51];
vector<pair<int, int>> home;
vector<pair<int, int>> chick;
int htoc[101][14];
vector<int> open;

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int tmp, tmp2;
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> map[i][j];
			if (map[i][j] == 1) {
				home.push_back({ i,j });
			}
			if (map[i][j] == 2) {
				chick.push_back({ i,j });
			}
		}
	}
	for (int i = 0; i < home.size(); i++) {
		for (int j = 0; j < chick.size(); j++) {
			htoc[i][j] = abs(home[i].first - chick[j].first) + abs(home[i].second - chick[j].second);
		}
	}
	for (int i = 0; i < chick.size(); i++) {
		if (i < M) {
			open.push_back(0);
		}
		else {
			open.push_back(1);
		}
	}
	do {
		tmp2 = 0;
		for (int i = 0; i < home.size(); i++) {
			tmp = 9999;
			for (int j = 0; j < chick.size(); j++) {
				if (open[j] == 0) {
					tmp = min(tmp, htoc[i][j]);
				}
			}
			tmp2 += tmp;
		}
		ans = min(tmp2, ans);
	} while (next_permutation(open.begin(), open.end()));

	cout << ans;
	return 0;
}

www.acmicpc.net/problem/15683

 

15683번: 감시

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감

www.acmicpc.net

처음에 단순히 각각의 최대값들만 계산하게 구현했다가 안되서 한동안 코테 공부를 소홀하게 만든 문제이다..

 

단순 구현 문제임에도 고려해야 될 사항들이 많은 문제라 많이 어려웠다.(내 생각엔 최악의 문제이다..ㅠㅠ)

 

고민하다가 결국 재귀로 해결할 수 있었고 모든 경우의 수를 브루트 포스와 비슷하게 다 고려하여 풀었다.

 

각 cctv의 시야 범위를 7로 바꾸는 부분은 바킹독님의 풀이를 참고하여 수정하였다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

int N, M, ans = 987654;

int map2[10][10];
int cam2[2][2] = { {1,3},{2,4} };
int cam3[4][2] = { {1,2},{2,3},{3,4},{1,4} };
int cam4[4][3] = { {1,2,3},{2,3,4},{1,3,4},{1,2,4} };
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1,0 };
vector<pair<int, int>> v;


void cpy(int(*map1)[10]) {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			map2[i][j] = map1[i][j];
		}
	}
}

void cpy2(int (*map1)[10]) {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			map1[i][j] = map2[i][j];
		}
	}
}

void check(int x, int y, int dir) {
	while (1) {
		x += dx[dir-1];
		y += dy[dir-1];
		if (x < 0 || y < 0 || x >= N || y >= M || map2[x][y] == 6) {
			return;
		}
		if (map2[x][y] != 0) {
			continue;
		}
		map2[x][y] = 7;
	}
}

void dfs(int cnt) {
	int map1[10][10];
	cpy2(map1);
	if (cnt == v.size()) {
		int k = 0;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				if (map2[i][j] == 0) {
					k++;
				}
			}
		}
		ans = min(ans, k);
		return;
	}
	
	int x = v[cnt].first;
	int y = v[cnt].second;
	if (map2[x][y] == 1) {
		for (int i = 1; i <= 4; i++) {
			cpy2(map1);
			check(x, y, i);
			cnt++;
			dfs(cnt);
			cnt--;
			cpy(map1);
		}
	}
	else if (map2[x][y] == 2) {
		for (int i = 0; i < 2; i++) {
			cpy2(map1);
			for (int j = 0; j < 2; j++) {
				check(x, y, cam2[i][j]);
			}
			cnt++;
			dfs(cnt);
			cnt--;
			cpy(map1);
		}
	}
	else if (map2[x][y] == 3) {
		for (int i = 0; i < 4; i++) {
			cpy2(map1);
			for (int j = 0; j < 2; j++) {
				check(x, y, cam3[i][j]);
			}
			cnt++;
			dfs(cnt);
			cnt--;
			cpy(map1);
		}
	}
	else if (map2[x][y] == 4) {
		cpy2(map1);
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 3; j++) {
				check(x, y, cam4[i][j]);
			}
			cnt++;
			dfs(cnt);
			cnt--;
			cpy(map1);
		}
	}
	else {
		cpy2(map1);
		for (int i = 1; i <= 4; i++) {
			check(x, y, i);
		}
		cnt++;
		dfs(cnt);
		cnt--;
		cpy(map1);
	}
	return;
}

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			cin >> map2[i][j];
			if (map2[i][j] != 0 && map2[i][j] != 6) {
				v.push_back({ i,j });
			}
		}
	}
	dfs(0);
	
	cout << ans;
}

www.acmicpc.net/problem/14499

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지도

www.acmicpc.net

처음엔 어떻게 풀어야할지 아예 감이 안잡히는 문제였다.

 

주사위를 구현해야하는데 방향까지 고려해서 구현을 해야한다고 생각을 했었다.

 

하지만 전개도 번호를 고정으로 잡고 그 안의 값들만 변경 시켜서 구현하니 바로 풀리는 문제였다.

 

각각 동서남북으로 이동하면서 조건 확인 및 좌표수정, 주사위 바닥면을 복사해주고, 주사위 면들을 옮겼다.

 

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

int N, M, x, y, K;
int map[21][21];
int dice[7];

void cp(int a) {	//주사위 바닥면 복사
	if (map[x][y] == 0) {
		map[x][y] = dice[a];
	}
	else {
		dice[a] = map[x][y];
		map[x][y] = 0;
	}
}

int main() {
	int z, tmp;
	cin >> N >> M >> x >> y >> K;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			cin >> map[i][j];
		}
	}
	for (int i = 0; i < K; i++) {
		cin >> z;
		if (z == 1) {	//동쪽 이동
			if (y + 1 < M) {
				y++;
				cp(3);
				tmp = dice[1];
				dice[1] = dice[3];
				dice[3] = dice[6];
				dice[6] = dice[4];
				dice[4] = tmp;
				cout << dice[6] << "\n";
			}
		}
		else if (z == 2) {	//서쪽 이동
			if (y - 1 >= 0) {
				y--;
				cp(4);
				tmp = dice[1];
				dice[1] = dice[4];
				dice[4] = dice[6];
				dice[6] = dice[3];
				dice[3] = tmp;
				cout << dice[6] << "\n";
			}
		}
		else if (z == 3) {		//북쪽 이동
			if (x - 1 >= 0) {
				x--;
				cp(2);
				tmp = dice[1];
				dice[1] = dice[2];
				dice[2] = dice[6];
				dice[6] = dice[5];
				dice[5] = tmp;
				cout << dice[6] << "\n";
			}
		}
		else {
			if (x + 1 < N) {	//남쪽 이동
				x++;
				cp(5);
				tmp = dice[1];
				dice[1] = dice[5];
				dice[5] = dice[6];
				dice[6] = dice[2];
				dice[2] = tmp;
				cout << dice[6] << "\n";
			}
		}
	}
	return 0;
}

'알고리즘 공부 > C++' 카테고리의 다른 글

[C++] 백준 15686번: 치킨 배달  (0) 2021.04.09
[C++] 백준 15683번: 감시  (0) 2021.04.08
[C++] 백준 16234번: 인구 이동  (0) 2021.03.25
[C++] 백준 14500번: 테트로미노  (0) 2021.03.25
[C++] 백준 3190번: 뱀  (0) 2021.03.19

www.acmicpc.net/problem/16234

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

구현 문제이자 BFS로서 생각보다 크게 어렵진 않았지만 배열 크기 선언과 범위 설정하느라

 

계속 틀리다가 맞추었다. N*N으로 배열을 선언해야하는데 그 생각을 못하고 그냥 N으로 선언한 것이 패착이였다.

 

실제 시험장에서는 항상 변수 선언을 할때 생각을 하면서 해야할거 같다..

 

풀이는 BFS로 연합의 개수를 세고 각 2차원 배열로 각 연합의 인구수를 계산해서 저장을 해놓고

 

마지막에 인구수를 나눈 값을 넣어주는 형식으로 구성했다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

int N, L, R, ans, cnt = 1;
int map[51][51];
int visit[51][51];
int people[2555][2];
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };
queue<pair<int, int>> q;

void bfs(int x, int y) {
	q.push({ x, y });
	visit[x][y] = cnt;
	people[cnt][1]++;
	people[cnt][0] += map[x][y];

	while (!q.empty()) {
		int s = q.size();
		for (int i = 0; i < s; i++) {
			int a = q.front().first;
			int b = q.front().second;
			q.pop();
			for (int j = 0; j < 4; j++) {		//국경을 열 수 있는지 확인
				int c = a + dx[j];
				int d = b + dy[j];
				if (c >= 0 && d >= 0 && c < N && d < N && visit[c][d] == 0 
					&& abs(map[a][b] - map[c][d]) >= L
					&& abs(map[a][b] - map[c][d]) <= R) {
					q.push({ c, d });
					visit[c][d] = cnt;
					people[cnt][0] += map[c][d];
					people[cnt][1]++;
				}
			}
		}
	}
	return;
}

int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	cin >> N >> L >> R;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> map[i][j];
		}
	}
	while (1) {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (visit[i][j] == 0) {
					bfs(i, j);
					cnt++;	//연합 갯수 증가
				}
			}
		}
		for (int i = 0; i < N; i++) {	//연합 안의 사람들 재분배
			for (int j = 0; j < N; j++) {
				if (visit[i][j] > 0) {
					map[i][j] = people[visit[i][j]][0] / people[visit[i][j]][1];
				}
			}
		}
		if (cnt > N * N) {	//연합의 갯수가 N*N이면 중단
			break;
		}
		ans++;
		cnt = 1;
		memset(people, 0, sizeof(people));	//배열 초기화
		memset(visit, 0, sizeof(visit));
	}
	cout << ans;
	return 0;
}

'알고리즘 공부 > C++' 카테고리의 다른 글

[C++] 백준 15683번: 감시  (0) 2021.04.08
[C++] 백준 14499번: 주사위 굴리기  (0) 2021.03.26
[C++] 백준 14500번: 테트로미노  (0) 2021.03.25
[C++] 백준 3190번: 뱀  (0) 2021.03.19
[C++] 백준 13458번: 시험 감독  (0) 2021.03.17

+ Recent posts