안경잡이개발자

728x90
반응형

문제 유형: 정수론

문제 URL: https://codeforces.com/contest/1055/problem/C


  앨리스(Alice)와 밥(Bob)은 모두 각각 일정한 컨디션 주기가 있습니다. 운이 좋은(Lucky) 날이 몇 일 계속된 이후에, 그 이후에는 운이 나쁜(Unlucky) 날이 몇 일간 계속되는 방식입니다. 이 때 앨리스와 밥이 둘 다 운이 좋은(Lucky) 날이 최대 몇 일 겹칠 수 있는지를 구하면 되는 문제입니다.


  예를 들어 앨리스는 0일부터 2일까지 운이 좋고, 주기가 5라고 가정합시다. 또한 밥은 1일부터 3일까지 운이 좋고, 주기가 5라고 가정합시다. 이 때 운이 좋은 날을 1로, 운이 나쁜 날을 0으로 표현하면 다음과 같습니다. 둘 다 운이 좋은 날은 최대 2일이군요.


  0 1 2 3 4 5 6 7 8 9

  1 1 1 0 0 1 1 1 0 0

  0 1 1 1 0 0 1 1 1 0


  이 문제는 날짜가 0일부터 10억 일까지 입력될 수 있다는 점에서 사실상 상수 시간 안에 답을 도출할 수 있는 알고리즘이 필요합니다. 이는 '특정 주기'로 반복된다는 점에서 조금만 생각해보면 쉽게 구할 수 있습니다.


  바로 앨리스의 주기와 밥의 주기의 최대 공약수(GCD)를 구하는 것입니다. 최대 공약수를 기준으로 앨리스 혹은 밥의 주기를 '슬라이딩'하여 운이 좋은 날이 최대 얼마나 겹치는지 확인할 수 있기 때문이죠. 예를 들어 최대 공약수가 3이 나왔다면, 앨리스 혹은 밥의 컨디션 주기를 3만큼 시프트(Shift) 할 수 있는 것입니다. 왼쪽 혹은 오른쪽으로요. 


  따라서 전체 4가지 경우만 고려하면 돼요.


  1) 좋은 운이 시작되는 날이 A가 B에 비해 먼저 있을 때

  1-1) A가 B의 최대한 왼쪽에 붙는 경우를 계산하기.

  1-2) A가 B의 최대한 오른쪽에 붙는 경우를 계산하기.


  2) 좋은 운이 시작되는 날이 B가 A에 비해 먼저 있을 때

  2-1) B가 A의 최대한 왼쪽에 붙는 경우를 계산하기.

  2-2) B가 A의 최대한 오른쪽에 붙는 경우를 계산하기.


  위 4가지 경우를 모두 고려하여 최대한 많이 겹칠 수 있는 일자를 구하면 됩니다. 따라서 유클리드 알고리즘을 이용하여 사실상 상수에 가까울 정도로 빠른 속도로 답을 도출할 수 있는 것입니다.


※ 입출력 예시 ※

input
Copy
0 2 5
1 3 5
output
Copy
2
input
Copy
0 1 3
2 3 6
output
Copy
1

※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>

#include <limits.h>


using namespace std;


int gcd(int a, int b) {

if(a == 0)

return b;

return gcd(b % a, a);

}


int main(void) {

int la, ra, ta, lb, rb, tb;

cin >> la >> ra >> ta >> lb >> rb >> tb;

int pat = gcd(ta, tb);

int res = 0;

int gap = abs(la - lb) / pat;

// a가 b보다 더 앞에 있을 때 

if(la < lb) {

int temp = la + pat * gap; // a가 b의 최대한 왼쪽에 붙는 경우 

res = max(res, (temp + 1 + (ra - la)) - lb); 

temp = la + pat * (gap + 1); // a가 b의 최대한 오른쪽에 붙는 경우 

res = max(res, (1 + rb) - temp); 

}

// b가 a보다 더 앞에 있을 때

else {

int temp = lb + pat * gap; // b가 a의 최대한 왼쪽에 붙는 경우

res = max(res, (temp + 1 + (rb - lb)) - la); 

temp = lb + pat * (gap + 1); // b가 a의 최대한 오른쪽에 붙는 경우 

res = max(res, (1 + ra) - temp);

}

// 두 사람의 운이 좋은 날이 계속되는 길이보다 커질 수는 없음 

res = min(res, rb - lb + 1);

res = min(res, ra - la + 1);

cout << res;

return 0;

}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1055/problem/B


  앨리스(Alice)의 머리카락들이 계속 자라고 있습니다. 앨리스는 그래서 미용사에게 찾아갔습니다. 앨리스는 모든 머리카락이 최대 l 센티미터(Centimeter)가 될 수 있도록 머리를 자르고 싶습니다. 앨리스의 머리카락 개수는 N개이며, 각 머리카락을 1번부터 N번까지의 번호로 다룬다고 합시다.


  미용사가 가위를 이용해 한 번 머리카락을 자르면, 미용사는 길이가 l보다 큰 머리카락의 길이를 l이 되도록 만들 수 있습니다. 이 때 머리카락은 1번부터 N번까지 차례대로 위치하며 가위로 서로 인접한 머리카락들을 한 번에 자를 수 있습니다. 미용사는 가능한 빠르게 앨리스의 머리를 손질하고 싶습니다. 미용사가 가위질을 할 때마다 1초가 소요된다고 가정했을 때, 앨리스의 머리를 손질하는 최소 시간을 구하면 되는 문제입니다.


  또한 앨리스는 미용사에게 언제 갈 지를 정확히 결정하지 않았기 때문에, 특정한 시각에 자신의 머리카락의 길이를 미용사에게 알려주면서 머리 손질 시간을 물어보고자 합니다. 이 때 정확히 두 가지 유형의 쿼리(Query)가 있습니다.


  0) 앨리스가 지금 미용사에게 가면 머리를 손질하는데 얼마나 시간이 걸릴 지 물어보는 쿼리입니다.

  1) P와 D가 이어서 입력되며, P번째 머리카락이 D 센티미터만큼 자랐다는 의미를 가지는 쿼리입니다.


  기본적으로 앨리스가 원하는 머리카락 최대 길이인 l은 프로그램이 끝날 때까지 동일한 값을 가진다고 보면 됩니다.


  이 문제는 대표적인 구현 문제 유형이라고 볼 수 있습니다. 또한 인접한 머리카락들을 잘 살펴보는 것이 중요합니다. 그러므로 일단 맨 처음에 주어진 초기 상태를 보고 머리카락을 몇 번 잘라야 하는지를 검사합니다. 이후에 특정한 위치의 머리카락이 길어질 때마다 그 왼쪽과 오른쪽에 있는 머리카락과의 관계를 확인하여, 가위질 횟수가 증가할 지 혹은 감소할 지를 판단하면 됩니다.


  총 9가지 경우의 수만 고려하면 돼요. 머리카락이 l보다 작다가, l보다 길어진 특정한 머리카락의 위치를 (K), 왼쪽과 오른쪽에 있는 머리카락이 각각 길다면 (긴), 짧다면 (짧)이라고 합시다. 또한 머리카락 중에서 K가 가장 왼쪽 혹은 오른쪽에 위치할 때, 끝 부분은 (끝)이라고 합시다.


  1) 짧K짧: K 위치를 잘라야 하므로 가위질 횟수 1 증가

  2) 짧K긴: 오른쪽 가위질에 포함될 수 있으므로 가위질 횟수 그대로

  3) 긴K짧: 왼쪽 가위질에 포함될 수 있으므로 가위질 횟수 그대로

  4) 긴K긴: 왼쪽과 오른쪽 가위질에 모두 포함될 수 있으므로 가위질 횟수 1 감소

  5) 끝K짧: K 위치를 잘라야 하므로 가위질 횟수 1 증가

  6) 끝K긴: 오른쪽 가위질에 포함될 수 있으므로 가위질 횟수 그대로

  7) 짧K끝: K 위치를 잘라야 하므로 가위질 횟수 1 증가

  8) 긴K끝: 왼쪽 가위질에 포함될 수 있으므로 가위질 횟수 그대로

  9) 끝K끝: 원소가 1개인 경우이므로 가위질 횟수 1 증가


  위 9가지 경우의 수를 고려하여 전체 가위질 최소 횟수를 구하는 프로그램을 작성하면 됩니다.


※ 입출력 예시 ※

input
Copy
4 7 3
1 2 3 4
0
1 2 3
0
1 1 3
0
1 3 1
0
output
Copy
1
2
2
1

※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0) 

#include <iostream>

using namespace std; long long a[100001]; long long n, m, l; int main(void) { cin >> n >> m >> l; int res = 0; cin >> a[0]; if(a[0] > l) { res++; } for(int i = 1; i < n; i++) { cin >> a[i]; // 초기 상태에서 가위질 횟수를 구합니다. if(a[i] > l) { if(a[i - 1] <= l) { res++; } } } for(int i = 0; i < m; i++) { int oper; cin >> oper; if(oper == 0) { cout << res << '\n'; } else { int x, length; cin >> x >> length; int count = 0; // 머리카락이 l보다 짧았다가, l보다 길어져서 '자를 머리카락'이 되는 경우 if(a[x - 1] <= l && a[x - 1] + length > l) { bool find = false; // 원소가 1개인 경우를 체크하기 위한 목적 if(x - 2 >= 0) { // 왼쪽 머리카락 확인 if(a[x - 2] <= l) count++; // (짧) else count--; // (긴) find = true; } if(x < n) { // 오른쪽 머리카락 확인 if(a[x] <= l) count++; // (짧) else count--; // (긴) find = true; } if(!find) res++; } if(count >= 1) res++; else if(count == -2) res--; a[x - 1] += length; } } return 0; }


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1055/problem/A


  일직선 형태로 존재하는 지하철 노선이 있다고 합니다. 노선에는 1부터 N까지 번호가 매겨진 N개의 역이 있습니다. 이 때 밥(Bob)은 1번 역에 살고 있으며, 엘리스(Alice)는 S번 역 근처에 살고 있습니다.


  지하철 노선은 두 개의 트랙으로 구성됩니다. 첫 번째 트랙에서는 열차가 역 1에서 역 N으로 이동합니다. 두 번째 트랙에서의 열차는 N부터 1까지 반대 방향으로 이동합니다. 또한 기차는 트랙의 끝에 도달한 순간 운행을 정지한다고 가정합니다.


  다만 일부 역에서는 열차가 멈출 수 없는 상황이라고 합니다. 그러므로 몇몇 역은 열차가 그냥 통과하여 지나간다고 가정합니다. 이러한 상황에서 밥(Bob)에게 각 역에 대해서 열차가 멈출 수 있는지, 없는지의 여부가 알려져 있다고 했을 때 밥이 엘리스의 집으로 갈 수 있는지를 출력하면 됩니다.


  예를 들어 역이 5개이고, 엘리스가 4번 역 근처에 살고 있다고 해봅시다. 이 때 두 개의 트랙에서 열차가 멈출 수 있는 역들은 1로, 멈출 수 없는 역들을 0으로 표현할 때 다음과 같다고 가정합시다.


  첫 번째 트랙: 1 0 0 0 1

  두 번째 트랙: 0 1 1 1 1


  이 때는 밥이 출발지인 1번 역에서 5번 역으로 이동한 뒤에, 5번 역에서 두 번째 트랙을 타고 4번 역으로 이동하여 엘리스의 집에 도착할 수 있습니다.


  이 문제는 정말 간단한 구현 문제입니다. 밥이 항상 1번 역에서 출발한다는 점을 기억하면 됩니다. 그래서 만약 1번 역이 운행되지 않는 상태라면 그 즉시 도달할 수 없음을 알리고 종료하면 됩니다. 이후에는 각 역을 하나씩 확인하며, 첫 번째 트랙에서 운행되는 역일 때만 특정한 조건을 검사하면 됩니다. 여기서 조건은 바로 해당 역이 엘리스가 있는 역인지 혹은 두 번째 트랙으로 이동하여 반대 방향으로 이동하여 엘리스가 있는 역으로 갈 수 있는지를 의미합니다.


  따라서 간단한 난이도의 단순 구현 문제라고 볼 수 있습니다.


※ 입출력 예시 ※

input
Copy
5 3
1 1 1 1 1
1 1 1 1 1
output
Copy
YES
input
Copy
5 4
1 0 0 0 1
0 1 1 1 1
output
Copy
YES
input
Copy
5 2
0 1 1 1 1
1 1 1 1 1
output
Copy
NO

※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream> using namespace std; int a[1001]; int b[1001]; int main(void) { int n, m; cin >> n >> m; for(int i = 1; i <= n; i++) { cin >> a[i]; } for(int i = 1; i <= n; i++) { cin >> b[i]; } if(a[1] == 0) { cout << "NO"; return 0; } for(int i = 1; i <= n; i++) { if(a[i] == 1) { if(i == m || (b[i] == 1 && b[m] == 1 && m < i)) { cout << "YES"; return 0; } } } cout << "NO"; return 0; }


728x90
반응형

728x90
반응형

문제 유형: 그리디

문제 URL: https://codeforces.com/contest/1075/problem/C


  문제를 간단히 요약하자면 다음과 같습니다.


  높이고 10^9이고, 너비가 10^9인 체스판이 있습니다. 이 때 가장 왼쪽 아래인 (1, 1)의 위치에 하나의 돌이 있습니다. 얘는 마차(Look)라서 이동할 때 상하좌우로 쭉쭉 이동할 수 있습니다. 이 돌은 체스판의 가장 위쪽에 도달하고자 합니다. y 좌표 상으로 10^9의 위치에 닿으면 되는 것이며 x 좌표는 어떤 값이 들어가도 상관 없습니다. 좌표를 나타낼 때는 (열, 행)과 같은 방식으로 표현합니다.


  이 때 체스판을 가로 막는 다양한 선들이 있다고 합니다. 선의 종류로는 수직선과 수평선이 있습니다.


  1) 수직선: 특정한 x 위치에서 수직으로 끝 없이 이어진 선입니다.

  2) 수평선: x1부터 x2까지의 열을 차지하는 선으로, 행 y에 위치합니다.


  수평선들은 수직 관계에 있는 다른 선들과는 x 좌표가 겹쳐도 됩니다. 하지만 수평한 수평선끼리는 x 좌표가 겹치지 않는다고 합니다. (1, 1)에 위치한 돌은 y 좌표 상으로 10^9의 위치에 닿을 때까지 이동할 수 있는데, 체스판에 있는 선들을 뚫고 나아갈 수 없다고 합니다. 따라서 선으로 가로 막힌 경우 선을 지우고 나아가야 합니다. 체스판의 가장 위쪽까지 도달하도록 선을 삭제할 때 최소한의 삭제할 선 개수를 구하면 됩니다.


※ 핵심 풀이 ※


  이 문제는 두 개의 사실만을 깨달으면 쉽게 풀 수 있고, 깨닫지 못하면 푸는 것이 굉장히 어렵습니다.


  ▶ 그것은 바로 수평선 중에서 왼쪽 x 좌표가 1이 아닌 것은 무시해도 된다는 것입니다.

  ▶ 그로 인해 수평선의 y 좌표는 신경 쓸 필요가 없다는 것까지 유추하면 됩니다.


  수평선의 왼쪽 x 좌표가 1이 아닌 것은 무시해도 좋은 이유를 설명해드리겠습니다. 이것은 '수평한 수평선끼리는 x 좌표가 겹치지 않는다'는 성질 때문에 가능한 것입니다.


  다음과 같은 경우를 생각해봅시다.



  몇 개의 선을 삭제해야 제일 위 칸으로 이동할 수 있을까요?


  정답은 바로 1개의 선입니다.



  위와 같이 중간에 있는 선을 지워버리면, 지그재그로 이동하여 제일 위 칸까지 이동할 수 있거든요. 결과적으로 왼쪽 x 좌표가 1인 수평선이 아니면 사실상 전혀 의미 없는 수평선이라고 할 수 있습니다. 돌 입장에서는 왼쪽 끝까지 편하게 이동할 수 있으니까요.



  결과적으로 기존의 시작할 때의 상태는 왼쪽 x 좌표가 1이 아닌 수평선들은 모두 지워버린 위 그림과 같은 상태와 같다고 볼 수 있습니다. 이렇게 정리된 상태에서 보니 한 가지 더 알게 되는 점이 있습니다. 그것은 바로 y 좌표는 굳이 신경 쓸 필요가 없다는 것입니다. 왜냐하면 어차피 하나의 y 좌표에는 최대 1개의 수평선만이 존재할 수 있을 테니까요. y 좌표는 고려 할 필요가 없습니다.


  이러한 원리를 이용하여, 각 수직선을 왼쪽부터 차례대로 확인하며 위를 덮는 수평선의 개수를 고려하여 정답을 도출하면 됩니다.


※ 예시 입출력 ※


input
Copy
2 3
6
8
1 5 6
1 9 4
2 4 2
output
Copy
1
input
Copy
1 3
4
1 5 3
1 9 4
4 6 6
output
Copy
1
input
Copy
0 2
1 1000000000 4
1 1000000000 2
output
Copy
2
input
Copy
0 0
output
Copy
0
input
Copy
2 3
4
6
1 4 3
1 5 2
1 6 5
output
Copy
2


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include 
#include 
#define MAX 100001

using namespace std;

int vertical[MAX], line[MAX];
int INF = 1e9;

int main()
{
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n; i++)
		cin >> vertical[i];
	vertical[n++] = INF;
	sort(vertical, vertical + n);
	int lineCount = 0;
	for(int i = 0; i < m; i++) {
		int a, b, c;
		cin >> a >> b >> c; 
		if (a != 1) // 왼쪽 x 좌표가 1인 수평선은 무시
			continue;
		line[lineCount++] = b;
	}
	sort(line, line + lineCount);
	int res = lineCount; // 시작 위치에서 돌이 위쪽으로만 뚫고 이동하는 경우를 초기 값으로 설정 
	int j = 0;
	// 각 수직선을 왼쪽부터 하나씩 확인하며 
	for(int i = 0; i < n; i++) {
		// 각 수직선을 덮는 수평선들의 개수를 세기 
		while (j < lineCount && line[j] < vertical[i]) {
			j++;
		}
		res = min(res, i + lineCount - j);
	}
	cout << res;
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 그리디

문제 URL: https://codeforces.com/contest/915/problem/C


  이 문제는 특정한 수가 주어졌을 때 그 수로 순열을 처리하는 문제입니다. 두 수 a와 b가 있다고 합시다. 이 때 a로 만들 수 있는 자릿수의 순열 중에서 b보다 크지 않은 가장 큰 수를 구해 출력하는 문제입니다. 입력과 출력으로는 0으로 시작하지 않는 양의 정수가 입력 됩니다. 이 때 자릿수의 순열이란, 수에서 숫자들의 자리를 서로 바꾸어 나타낼 수 있는 수들을 의미합니다.


  b보다 크지 않은 가장 큰 수를 구해야 하므로 맨 왼쪽부터 가장 큰 숫자가 나올 수 있도록 하나씩 숫자를 배정하면 문제를 해결할 수 있습니다. 이 때 가장 중요한 것은 단순히 들어갈 수 있는 가장 큰 숫자를 넣는다고 되지는 않습니다. 그렇게 하면 답을 못 구할 수도 있어요. 숫자를 하나씩 넣을 때마다 최소 크기 접미사를 붙여서 b보다 작은지 검사하는 로직이 추가적으로 필요합니다.


  a: 123456789123456789

  b: 276193619183618162


  왜 최소 크기 접미사를 구해야 하는지는 위의 예시를 통해 확인할 수 있습니다.


  단순히 들어갈 수 있는 가장 큰 숫자를 계속해서 배정하면 '276193619'에서 더이상 진행할 수 없습니다. 그 뒤에 오는 b의 숫자가 1이므로, 문자열 a에 포함된 숫자 중에서 넣을 수 있는 1보다 작거나 같은 숫자는 더 이상 존재하지 않기 때문이에요. 그러므로 9 대신 그 다음 작은 숫자인 8이 들어가서 '2761936189~'로 진행되는 방식으로 처리해야 합니다. 이를 효과적으로 판단하기 위해서 최소 크기 접미사를 넣어보는 겁니다.


  9를 넣는 경우와 8을 넣는 경우를 비교해봅시다. 왼쪽의 수는 우리가 찾을 해답이고, 오른쪽에 있는 수는 b를 의미합니다.


  276193619234455788 > 276193619183618162 (불가능)

  276193618234455789 < 276193619183618162 (가능)


  9를 넣는 경우와 8을 넣는 경우 각각에 대해서 최소 접미사를 붙인 경우는 위와 같습니다. 9가 들어가는 경우에는 최소 크기 접미사를 붙였을 때, b보다 더 커지므로 불가능한 것입니다. 반면에 그보다 더 작은 숫자인 8부터 다시 넣어서 최소 크기 접미사를 붙여보니 b보다 작거나 같으므로 8은 들어갈 수 있는 것입니다. 결과적으로 기본적으로 왼쪽부터 최대한 큰 숫자를 채워 넣고, 해당 숫자가 들어갈 수 있는 지의 여부는 최소 접미사를 통해 확인할 수 있는 것입니다.


※ 예제 입출력 ※

input
Copy
123
222
output
Copy
213
input
Copy
3921
10000
output
Copy
9321
input
Copy
4940
5000
output
Copy
4940


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#define MAX 10

using namespace std;

string a, b;
int d[MAX];
string res;

// 남은 뒷 자릿들을 '가장 작은 수'가 되도록 채웁니다. 
void makeSmall(int from)
{
	for (int i = 0; i < MAX; i++)
		for (int j = 0; j < d[i]; j++)
			res[from++] = i + '0';
}

int main()
{
	cin >> a;
	cin >> b;
	for (int i = 0; i < a.length(); i++)
		d[a[i] - '0']++;
	int zeros = 0;
	// A가 B보다 자릿수가 작은 경우 앞에 0을 채웁니다. 
	while (a.length() < b.length()) {
		a = "0" + a;
		zeros++;
	}
	res = a;
	// '원래 A의 길이'만큼 반복합니다. 
	for (int i = zeros; i < a.length(); i++) {
		// 9부터 0까지의 숫자 중에서 들어갈 수 있는 것을 고릅니다. 
		for (int j = MAX - 1; j >= 0; j--) {
			// 일단 넣어 봅니다. 
			if (d[j] > 0) {
				d[j]--;
				res[i] = j + '0';
				// 현재 넣은 숫자를 토대로 뒷자리들은 최대한 작게 구성합니다.
				// 이렇게 만들어진 수가 가능한 수라면 break;하여 해당 자릿수 구성을 완료합니다. 
				makeSmall(i + 1);
				cout << i << ":" << res << '\n';
				if (res <= b) break;
				d[j]++;
			}	
		}
	}
	// 앞에 들어가는 0은 무시하고 출력합니다. 
	int pos = 0;
	while (res[pos] == '0') pos++;
	cout << res.substr(pos);
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1075/problem/B


  끝 없이 이어지는 좌표 상의 선으로 구성된 도시가 있습니다. 이 중에서 매일 도시 거주자들을 태워주는 M명의 택시 기사가 있습니다. 전체 구성원은 N명입니다. 택시 기사를 포함해 모든 N명의 사람들은 고유한 위치에 거주하고 있습니다.


  어떠한 사람이 택시를 부르면, 모든 택시 기사에게 전화를 하는 것이 아니라 자신과 가장 가까운 택시 기사 한 명만 부르게 됩니다. 만약에 거리가 동일한 택시기사가 여러 명이라면, 좌표 상에서 가장 작은 위치를 가진 택시 기사를 부르게 됩니다.


  어느 날 아침에, 택시 기사들은 하나의 의문이 생겼습니다. "매일 각 택시 기사들은 몇 명에게 전화를 받을까?"입니다. 이에 대한 답을 알려주는 프로그램을 작성하면 됩니다.


  예를 들어 일반 주민이 3명, 택시기사가 3명이라고 해봅시다. 이러면 전체 사람이 6명이므로 각각 위치가 주어지고, 각 위치에 따라서 해당 사람이 택시기사라면 1을, 아니라면 0이도록 입력이 주어지는 것입니다.


  전체 사람들의 위치: 2 3 4 5 6 7

  택시 기사들의 위치: 1 0 1 0 1 0


  이 때 택시 기사 3명은 각각 1명씩 전화를 받게 됩니다. 3번 위치의 사람은 2번 위치의 택시 기사에게, 5번 위치의 사람은 4번 위치의 택시 기사에게, 7번 위치의 사람은 6번 위치의 택시 기사에게 말입니다.


  이 문제는 간단히 크기가 2인 슬라이딩 윈도우를 이용해 풀 수 있습니다. 택시기사를 앞에서부터 두 명씩 묶어서 확인하면 돼요. 모든 사람들에 대해서 한 명씩 확인을 하는 건데요. 왼쪽 기사와 오른쪽 기사 사이에 있는 사람은, 더 가까운 기사에게 전화를 걸도록 처리하면 됩니다. 더 뒤쪽에 있는 택시기사의 위치보다 현재 사람의 위치가 크다면 슬라이딩을 수행하여, 그 이후의 택시기사 2명을 보는 방식을 사용합니다.


※ 입출력 예시 ※


input
Copy
3 1
1 2 3 10
0 0 1 0
output
Copy
3 
input
Copy
3 2
2 3 4 5 6
1 0 0 0 1
output
Copy
2 1 
input
Copy
1 4
2 4 6 10 15
1 1 1 1 0
output
Copy
0 0 0 1 

※ 정답 소스코드 ※
(컴파일 환경: GNU G++11 5.1.0)
#include <iostream>
#include <vector>

using namespace std;

vector a;
vector taxi;
vector res;

int main(void) {
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n + m; i++) {
		int x;
		cin >> x;
		a.push_back(x);
	}
	for(int i = 0; i < n + m; i++) {
		int x;
		cin >> x;
		if(x == 1) {
			taxi.push_back(a[i]);
			res.push_back(-1); // 택시 기사도 주민으로 보므로 초기에 -1로 설정 
		}
	}
	// 택시 기사가 1명이면, 크기가 2인 윈도우를 만들 수 없어 바로 답 출력 후 종료 
	if(taxi.size() == 1) {
		cout << a.size() - 1;
		return 0;
	}
	int now = 0;
	int start = taxi[now];
	int end = taxi[now + 1];
	for(int i = 0; i < a.size(); i++) {
		while(a[i] > end) {
			// 범위 한 칸 슬라이드 
			now++;
			// 마지막 범위인 경우 
			if(now == taxi.size() - 1) {
				// 남은 주민들을 마지막 택시에게 배정하고 종료 
				res[now] += a.size() - i;
				break;
			}
			start = taxi[now];
			end = taxi[now + 1];
		}
		if(now == taxi.size() - 1) break;
		// 현재 주민이 왼쪽과 가까운지, 오른쪽과 가까운지 판별 
		if(a[i] - start <= end - a[i]) {
			res[now]++;
		}
		else {
			res[now + 1]++;
		}
	}
	for(int i = 0; i < res.size(); i++) {
		cout << res[i] << ' ';
	}
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1075/problem/A


  N x N의 체스 판이 있을 때 (1, 1)에 '화이트 킹'이 있고, (N, N)에 '블랙 킹'이 있습니다. 각 킹은 인접한 8가지 방향의 셀 중 한 곳으로 이동할 수 있습니다. 대각선도 포함해 이동할 수 있는 것입니다. 이 때 (X, Y)의 위치에 화이트 킹과 블랙 킹 중에서 누가 더 먼저 도착할 수 있는지를 구하면 되는 문제입니다. 다만 화이트 킹이 먼저 이동하며 시작과 동시에 킹의 위치와 (X, Y)가 동일하다면 그 즉시 종료됩니다.


  이 문제는 매우 간단한 문제입니다. (1, 1)에 화이트 킹이 있으며 (N, N)에 블랙 킹이 있으므로, 두 위치에서 (X, Y) 까지의 이동 횟수를 구하면 됩니다. 횟수를 구할 때는 각 킹들이 대각선 방향으로도 이동할 수 있다는 점에서 '가로 길이와 세로 길이 중에서 더 긴 것'을 구하면 됩니다.


  이동 횟수: 가로 길이와 세로 길이 중에서 더 긴 것


  결과적으로 이동 횟수를 비교하여 정답을 출력하면 됩니다.


※ 입출력 예시 ※


input
Copy
4
2 3
output
Copy
White
input
Copy
5
3 5
output
Copy
Black
input
Copy
2
2 2
output
Copy
Black


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <stdio.h>

using namespace std;

int main(void) {
	long long int n;
	cin >> n;
	long long int x, y;
	cin >> x >> y;
	long long int black = max(n - x, n - y);
	long long int white = max(x - 1, y - 1);
	if(black == 0) {
		cout << "Black";
	}
	else if(white == 0) {
		cout << "White";
	}
	else if(white <= black) {
		cout << "White";
	}
	else {
		cout << "Black";
	}
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 수학

문제 URL: https://codeforces.com/contest/915/problem/A


  정원은 길이가 K인 선분(Segment) 형태로 표현됩니다. 또한 N개의 그릇에 물을 담아서 농작물에 물을 주려고 합니다. i번째 그릇은 한 시간에 Ai 길이의 연속적인 공간에 물을 줄 수 있도록 해줍니다. 이 때 한 번 물을 준 곳은 더 이상 주지 않으며 주인공은 하나의 그릇을 선택하여 가능한 빠르게 농사를 짓는 전체 공간에 물을 주고자 합니다. 이 때 주인공이 가장 적은 시간으로 정원에 물을 줄 때의 시간을 구하면 됩니다.


  이 문제는 길이가 K인 전체 정원에 물을 주어야 하며 남는 공간이 없어야 하고, 한 번 물을 준 곳은 더 이상 줄 수 없다는 점에서 단순히 약수를 판별하는 문제입니다. 주어진 그릇의 크기 중에서 K의 약수로 가장 큰 것을 고르면 됩니다. 따라서 K를 해당 약수로 나눈 값을 출력하도록 하면 정답 처리를 받을 수 있습니다. 또한 항상 하나 이상의 그릇은 정원의 모든 공간에 물을 줄 수 있도록 입력이 주어진다고 합니다.


※ 예시 입출력 ※

input
Copy
3 6
2 3 5
output
Copy
2
input
Copy
6 7
1 2 3 4 5 6
output
Copy
7


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <limits.h>

using namespace std;

int main(void) {
	int n, k;
	cin >> n >> k;
	int res = INT_MIN;
	for(int i = 0; i < n; i++) {
		int x;
		cin >> x;
		if(k % x == 0) {
			res = max(res, x);
		}
	}
	cout << k / res;
	return 0;
}


728x90
반응형