728x90

Hashing 성공서브태스크

 
 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 512 MB 55186 16890 14599 30.385%

문제

APC에 온 것을 환영한다. 만약 여러분이 학교에서 자료구조를 수강했다면 해시 함수에 대해 배웠을 것이다. 해시 함수란 임의의 길이의 입력을 받아서 고정된 길이의 출력을 내보내는 함수로 정의한다. 해시 함수는 무궁무진한 응용 분야를 갖는데, 대표적으로 자료의 저장과 탐색에 쓰인다.

이 문제에서는 여러분이 앞으로 유용하게 쓸 수 있는 해시 함수를 하나 가르쳐주고자 한다. 먼저, 편의상 입력으로 들어오는 문자열에는 영문 소문자(a, b, ..., z)로만 구성되어있다고 가정하자. 영어에는 총 26개의 알파벳이 존재하므로 a에는 1, b에는 2, c에는 3, ..., z에는 26으로 고유한 번호를 부여할 수 있다. 결과적으로 우리는 하나의 문자열을 수열로 변환할 수 있다. 예를 들어서 문자열 "abba"은 수열 1, 2, 2, 1로 나타낼 수 있다.

해시 값을 계산하기 위해서 우리는 문자열 혹은 수열을 하나의 정수로 치환하려고 한다. 간단하게는 수열의 값을 모두 더할 수도 있다. 해시 함수의 정의에서 유한한 범위의 출력을 가져야 한다고 했으니까 적당히 큰 수 M으로 나눠주자. 짜잔! 해시 함수가 완성되었다. 이를 수식으로 표현하면 아래와 같다.

 �=∑�=0�−1��mod�

해시 함수의 입력으로 들어올 수 있는 문자열의 종류는 무한하지만 출력 범위는 정해져있다. 다들 비둘기 집의 원리에 대해서는 한 번쯤 들어봤을 것이다. 그 원리에 의하면 서로 다른 문자열이더라도 동일한 해시 값을 가질 수 있다. 이를 해시 충돌이라고 하는데, 좋은 해시 함수는 최대한 충돌이 적게 일어나야 한다. 위에서 정의한 해시 함수는 알파벳의 순서만 바꿔도 충돌이 일어나기 때문에 나쁜 해시 함수이다. 그러니까 조금 더 개선해보자.

어떻게 하면 순서가 달라졌을때 출력값도 달라지게 할 수 있을까? 머리를 굴리면 수열의 각 항마다 고유한 계수를 부여하면 된다는 아이디어를 생각해볼 수 있다. 가장 대표적인 방법은 항의 번호에 해당하는 만큼 특정한 숫자를 거듭제곱해서 곱해준 다음 더하는 것이 있다. 이를 수식으로 표현하면 아래와 같다.

 �=∑�=0�−1����mod�

보통 r과 M은 서로소인 숫자로 정하는 것이 일반적이다. 우리가 직접 정하라고 하면 힘들테니까 r의 값은 26보다 큰 소수인 31로 하고 M의 값은 1234567891(놀랍게도 소수이다!!)로 하자.

이제 여러분이 할 일은 위 식을 통해 주어진 문자열의 해시 값을 계산하는 것이다. 그리고 이 함수는 간단해 보여도 자주 쓰이니까 기억해뒀다가 잘 써먹도록 하자.

입력

첫 줄에는 문자열의 길이 L이 들어온다. 둘째 줄에는 영문 소문자로만 이루어진 문자열이 들어온다.

입력으로 주어지는 문자열은 모두 알파벳 소문자로만 구성되어 있다.

출력

문제에서 주어진 해시함수와 입력으로 주어진 문자열을 사용해 계산한 해시 값을 정수로 출력한다.

Small (50점)

  • 1 ≤ L ≤ 5

Large (50점)

  • 1 ≤ L ≤ 50

예제 입력 1 복사

5
abcde

예제 출력 1 복사

4739715

예제 입력 2 복사

3
zzz

예제 출력 2 복사

25818

예제 입력 3 복사

1
i

예제 출력 3 복사

9

 

힌트

예제 1: abcde의 해시 값은 1 × 310 + 2 × 311 + 3 × 312 + 4 × 313 + 5 × 314 = 1 + 62 + 2883 + 119164 + 4617605 = 4739715이다.

예제 2: zzz의 해시 값은 26 × 310 + 26 × 311 + 26 × 312 = 26 + 806 + 24986 = 25818이다.

출처

University > 아주대학교 > 2018 Ajou Programming Contest > Division 2 C번

 

문제의 핵심은 변수 범위를 초과하지 않게 제곱 계산을 하는 것 인듯 하다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const int M = 1234567891;
const int R = 31;

long square_mod(int times) {
	long temp = 1;
	for (int i = 0; i < times; i++) { //returns R^times mod (M)
		temp *= R;
		if (temp >= M) {
			temp %= M;
		}
	}
	return temp;
}

int hash(char* target, int size) {
	long temp = 0;
	//수로 바꾸기
	for (int i = 0; i < size; i++) {
		target[i] = (int)target[i] - 96;
	}
	for (int i = 0; i < size; i++) {
		temp += target[i] * square_mod(i);
		if (temp >= M) {
			temp %= M;
		}
	}
	return temp;
}

int main() {
	int size = 0;
	scanf("%d", &size);
	getchar();//개행 문자 제거

	char* target_string = (char*)malloc(sizeof(char) * size+1); 
	if (target_string == NULL)
		return -1;
	
	fgets(target_string, size + 1, stdin);
	target_string[strcspn(target_string, "\n")] = 0;

	printf("%d", hash(target_string, size));

	free(target_string);

	return 0;
}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 11651 좌표 정렬하기 2  (1) 2024.02.28
[백준] 11650 좌표 정렬하기  (2) 2024.02.25
[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
728x90

좌표 정렬하기 2 성공

 
 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 256 MB 76620 49947 42604 66.855%

문제

2차원 평면 위의 점 N개가 주어진다. 좌표를 y좌표가 증가하는 순으로, y좌표가 같으면 x좌표가 증가하는 순서로 정렬한 다음 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

출력

첫째 줄부터 N개의 줄에 점을 정렬한 결과를 출력한다.

예제 입력 1 복사

5
0 4
1 2
1 -1
2 2
3 3

예제 출력 1 복사

 

1 -1
1 2
2 2
3 3
0 4

 

해당 문제는 c의 stdlib헤더의 qsort함수를 사용하면 쉽게 풀 수 있다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

typedef struct Coordinate {
	int x;
	int y;
}MyCoordinate;

int compare(const void* a, const void* b) {
	MyCoordinate* A = (MyCoordinate*)a;
	MyCoordinate* B = (MyCoordinate*)b;
	if (A->y != B->y)
		return A->y - B->y;
	else
		return A->x - B->x;
}

int main() {
	int size;
	scanf("%d", &size);
	MyCoordinate* p = (MyCoordinate*)malloc(sizeof(MyCoordinate) * size);

	for (int i = 0; i < size; i++) {
		scanf("%d %d", &(p[i].x), &(p[i].y));
	}

	qsort(p, size, sizeof(MyCoordinate), compare);

	for (int i = 0; i < size; i++) {
		printf("%d %d\n", p[i].x, p[i].y);
	}


}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 15829 Hashing  (3) 2024.02.28
[백준] 11650 좌표 정렬하기  (2) 2024.02.25
[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
728x90

좌표 정렬하기 

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 256 MB 136597 65405 50827 48.023%

문제

2차원 평면 위의 점 N개가 주어진다. 좌표를 x좌표가 증가하는 순으로, x좌표가 같으면 y좌표가 증가하는 순서로 정렬한 다음 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

출력

첫째 줄부터 N개의 줄에 점을 정렬한 결과를 출력한다.

예제 입력 1 복사

5
3 4
1 1
1 -1
2 2
3 3

예제 출력 1 복사

1 -1
1 1
2 2
3 3
3 4

 

#include <iostream>
#include <stdlib.h>
#include <algorithm>
using namespace std;

typedef struct Coordinate {
	int x;
	int y;
}UserCoordinate;

bool compare_coordinate(const Coordinate& a, const Coordinate& b) {
	if (a.x != b.x)
		return a.x < b.x;
	return a.y < b.y;
}

int main() {
	int size = 0;
	cin >> size;

	UserCoordinate* p = (UserCoordinate*)malloc(sizeof(UserCoordinate) * size);

	for (int i = 0; i < size; i++) {
		cin >> p[i].x;
		cin >> p[i].y;
	}

	sort(p, p + size, compare_coordinate);

	for (int i = 0; i < size; i++) {
		cout << p[i].x << " ";
		cout << p[i].y << "\n";
	}

	free(p);

	return 0;
}

 

c언어와 달리 c++의 sort(algorithm 헤더에 정의)함수는 포인터 접근이 아닌 값에 의한 접근을 하므로 

compare함수의 인자는 포인터(*)가 아닌 값(&)으로 정의해야 한다.

 

부등호의 방향은 오름차순 기준으로 정렬이 필요없는 경우 1을 반환하도록 작성해야 한다.

(c의 qsort의 경우 정렬이 필요없는 경우 음수를 반환하도록)

728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 15829 Hashing  (3) 2024.02.28
[백준] 11651 좌표 정렬하기 2  (1) 2024.02.28
[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
728x90

숫자 카드 2 

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 256 MB 138996 53768 38424 37.414%

문제

숫자 카드는 정수 하나가 적혀져 있는 카드이다. 상근이는 숫자 카드 N개를 가지고 있다. 정수 M개가 주어졌을 때, 이 수가 적혀있는 숫자 카드를 상근이가 몇 개 가지고 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

셋째 줄에는 M(1 ≤ M ≤ 500,000)이 주어진다. 넷째 줄에는 상근이가 몇 개 가지고 있는 숫자 카드인지 구해야 할 M개의 정수가 주어지며, 이 수는 공백으로 구분되어져 있다. 이 수도 -10,000,000보다 크거나 같고, 10,000,000보다 작거나 같다.

출력

첫째 줄에 입력으로 주어진 M개의 수에 대해서, 각 수가 적힌 숫자 카드를 상근이가 몇 개 가지고 있는지를 공백으로 구분해 출력한다.

예제 입력 1 복사

10
6 3 2 10 10 10 -10 -10 7 3
8
10 9 -5 2 3 4 5 -10

예제 출력 1 복사

3 0 0 1 2 0 0 2

 

 

c++ algorithm 라이브러리에는 binary_search 함수가 존재하지만, 이 함수는 bool타입을 반환하며, 존재 유무만 알려준다.

따라서, upper_bound, lower_bound 함수를 이용하여

찾는 값보다 최초로 큰 값을 가지는 멤버의 포인터(upper_bound) - 최초로 찾는 값을 가지는 멤버의 포인터(lower_bound)를 하면 c++에서 멤버의 크기를 고려해 나눠줘 우리가 찾는 값의 개수를 개산할 수 있다.

 

물론, 위 로직을 수행하기 위해선 오름차순 혹은 내림차순 정렬이 되어있어야 한다.

 

#include <iostream>
#include <stdlib.h>
#include <algorithm>
using namespace std;


int main() {
	int size_of_data_table = 0;
	cin >> size_of_data_table;
	int* data_table = (int*)malloc(sizeof(int) * size_of_data_table);
	for (int i = 0; i < size_of_data_table; i++) {
		cin >> data_table[i];
	}
	sort(data_table, data_table + size_of_data_table);

	int size_of_search_table = 0;
	cin >> size_of_search_table;
	int* search_table = (int*)malloc(sizeof(int) * size_of_search_table);
	for (int i = 0; i < size_of_search_table; i++) {
		cin >> search_table[i];
	}
	
	for (int i = 0; i < size_of_search_table; i++) {
		cout << upper_bound(data_table, data_table + size_of_data_table, search_table[i]) - lower_bound(data_table, data_table + size_of_data_table, search_table[i]) << " ";
	}


	free(data_table);
	free(search_table);
	
	return 0;
}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 11651 좌표 정렬하기 2  (1) 2024.02.28
[백준] 11650 좌표 정렬하기  (2) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
[백준] 1436 영화감독 숌  (1) 2024.02.10
728x90

분해합 

한국어   

 

시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 192 MB 148795 68780 54079 45.415%

문제

어떤 자연수 N이 있을 때, 그 자연수 N의 분해합은 N과 N을 이루는 각 자리수의 합을 의미한다. 어떤 자연수 M의 분해합이 N인 경우, M을 N의 생성자라 한다. 예를 들어, 245의 분해합은 256(=245+2+4+5)이 된다. 따라서 245는 256의 생성자가 된다. 물론, 어떤 자연수의 경우에는 생성자가 없을 수도 있다. 반대로, 생성자가 여러 개인 자연수도 있을 수 있다.

자연수 N이 주어졌을 때, N의 가장 작은 생성자를 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N(1 ≤ N ≤ 1,000,000)이 주어진다.

출력

첫째 줄에 답을 출력한다. 생성자가 없는 경우에는 0을 출력한다.

예제 입력 1 복사

216

예제 출력 1 복사

198

 

입력이 1,000,000까지 가능하고, 사용자에게로부터 입력받은 수를 생성하는 수를 구해야 한다.

따라서, 어떤수를 입력에 넣고 그 수의 분해합을 구하는 프로그램을 작성하되, 여섯자리 수까지만 고려하면 된다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int calculate_num(int num) {
	int six, five, four, three, two, one;
	six = num / 100000;
	five = num / 10000 - six*10;
	four = num / 1000 - six * 100 - five*10;
	three = num / 100 - six * 1000 - five*100 - four*10;
	two = num / 10 - six*10000 - five*1000 - four*100 - three*10;
	one = num - six*100000 - five*10000 - four*1000 - three*100 - two*10;

	return num + six + five + four + three + two + one;
}

int main() {
	int num;
	scanf("%d", &num);

	for (int i = 1; i <= num; i++) {
		if (calculate_num(i) == num) {
			printf("%d", i);
			return 0;
		}
			
	}
	printf("0");
	return 0;
}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 11650 좌표 정렬하기  (2) 2024.02.25
[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 1546 평균  (1) 2024.02.15
[백준] 1436 영화감독 숌  (1) 2024.02.10
[백준] 1259 팬린드롬수  (1) 2024.02.10
728x90
#    define ATOMIC_ADD(type, var, val) ATOMIC_ADD_##type(var, val)

위 전처리문은 메타 프로그래밍 기법으로, 코드를 작성하는 코드이다.

ATOMIC_ADD(int, a, 5);
ATOMIC_ADD(float, b, 4.5);

위와 같이 선언할 시, 

 

ATOMIC_ADD(type, var, val)은 ATOMIC_ADD_##type(var,val) 로 정의되었으므로

각각

ATOMIC_ADD_int(a,5);

ATOMIC_ADD_float(b,4.5);

와 같이 정의된다.

 

이와 같은 메타 프로그래밍 기법은 코드의 유연성과 확장성을 높이며, 타입별 연산 함수와 같이 여러 버전이 필요할 경우 관리하기 편하게 만들어준다.

728x90
728x90

평균 

시간 제한             메모리 제한                   제출                       정답                         맞힌 사람                    정답 비율

 

2 초 128 MB 267460 134561 109518 49.876%

문제

세준이는 기말고사를 망쳤다. 세준이는 점수를 조작해서 집에 가져가기로 했다. 일단 세준이는 자기 점수 중에 최댓값을 골랐다. 이 값을 M이라고 한다. 그리고 나서 모든 점수를 점수/M*100으로 고쳤다.

예를 들어, 세준이의 최고점이 70이고, 수학점수가 50이었으면 수학점수는 50/70*100이 되어 71.43점이 된다.

세준이의 성적을 위의 방법대로 새로 계산했을 때, 새로운 평균을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 시험 본 과목의 개수 N이 주어진다. 이 값은 1000보다 작거나 같다. 둘째 줄에 세준이의 현재 성적이 주어진다. 이 값은 100보다 작거나 같은 음이 아닌 정수이고, 적어도 하나의 값은 0보다 크다.

출력

첫째 줄에 새로운 평균을 출력한다. 실제 정답과 출력값의 절대오차 또는 상대오차가 10-2 이하이면 정답이다.

예제 입력 1 복사

3
40 80 60

예제 출력 1 복사

75.0

예제 입력 2 복사

3
10 20 30

예제 출력 2 복사

66.666667

10-2 이하의 오차를 허용한다는 말은 정확히 소수 2번째 자리까지 출력하라는 뜻이 아니다.

예제 입력 3 복사

4
1 100 100 100

예제 출력 3 복사

75.25

예제 입력 4 복사

5
1 2 4 8 16

예제 출력 4 복사

38.75

예제 입력 5 복사

2
3 10

예제 출력 5 복사

65.0

예제 입력 6 복사

4
10 20 0 100

예제 출력 6 복사

32.5

예제 입력 7 복사

1
50

예제 출력 7 복사

100.0

예제 입력 8 복사

9
10 20 30 40 50 60 70 80 90

예제 출력 8 복사

55.55555555555556

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
	int num;
	double max, sum, result;
	
	scanf("%d", &num);
	double* val = (double*)malloc(sizeof(double) * num);
	scanf("%lf", &val[0]);
	max = val[0];
	sum = val[0];
	for (int i = 1; i < num; i++) {
		scanf("%lf", &val[i]);
		if (max < val[i])
			max = val[i];
		sum += val[i];
	}
	result = (sum / ((double)num * max)) * 100.0;
	printf("%lf", result);
	free(val);
}
728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 10816 숫자 카드 2  (1) 2024.02.25
[백준] 2231 분해합  (0) 2024.02.22
[백준] 1436 영화감독 숌  (1) 2024.02.10
[백준] 1259 팬린드롬수  (1) 2024.02.10
[백준] 1002번 터렛  (0) 2023.12.26
728x90

영화감독 숌 

 
시간 제한          메모리 제한                    제출                         정답                         맞힌 사람           정답 비율

 

2 초 128 MB 93940 54189 43787 57.363%

문제

666은 종말을 나타내는 수라고 한다. 따라서, 많은 블록버스터 영화에서는 666이 들어간 제목을 많이 사용한다. 영화감독 숌은 세상의 종말 이라는 시리즈 영화의 감독이다. 조지 루카스는 스타워즈를 만들 때, 스타워즈 1, 스타워즈 2, 스타워즈 3, 스타워즈 4, 스타워즈 5, 스타워즈 6과 같이 이름을 지었고, 피터 잭슨은 반지의 제왕을 만들 때, 반지의 제왕 1, 반지의 제왕 2, 반지의 제왕 3과 같이 영화 제목을 지었다. 하지만 숌은 자신이 조지 루카스와 피터 잭슨을 뛰어넘는다는 것을 보여주기 위해서 영화 제목을 좀 다르게 만들기로 했다.

종말의 수란 어떤 수에 6이 적어도 3개 이상 연속으로 들어가는 수를 말한다. 제일 작은 종말의 수는 666이고, 그 다음으로 큰 수는 1666, 2666, 3666, .... 이다. 따라서, 숌은 첫 번째 영화의 제목은 "세상의 종말 666", 두 번째 영화의 제목은 "세상의 종말 1666"와 같이 이름을 지을 것이다. 일반화해서 생각하면, N번째 영화의 제목은 세상의 종말 (N번째로 작은 종말의 수) 와 같다.

숌이 만든 N번째 영화의 제목에 들어간 수를 출력하는 프로그램을 작성하시오. 숌은 이 시리즈를 항상 차례대로 만들고, 다른 영화는 만들지 않는다.

입력

첫째 줄에 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.

예제 입력 1 복사

2

예제 출력 1 복사

1666

예제 입력 2 복사

3

예제 출력 2 복사

2666

예제 입력 3 복사

6

예제 출력 3 복사

5666

예제 입력 4 복사

187

예제 출력 4 복사

66666

예제 입력 5 복사

500

예제 출력 5 복사

166699

 

브루트포싱 알고리즘이다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int calculate(int num);
int check_6(int num);

int main() {
	int user_input;
	scanf("%d", &user_input);
	printf("%d", calculate(user_input));
	return 0;
}

int calculate(int num) {
	int count = 0;
	
	for (int i = 1; i < 2147483647; i++) {
		if (check_6(i))
			count++;
		if (count == num)
			return i;
	}
}


int check_6(int num) {
	char string[11];
	snprintf(string, sizeof(string), "%d", num);
	for (int i = 0; i <= 7; i++) {
		if (string[i] == '6' && string[i + 1] == '6' && string[i + 2] == '6')
			return 1;
	}
	return 0;
}

 

 

 

728x90

'C, C++ > 백준' 카테고리의 다른 글

[백준] 2231 분해합  (0) 2024.02.22
[백준] 1546 평균  (1) 2024.02.15
[백준] 1259 팬린드롬수  (1) 2024.02.10
[백준] 1002번 터렛  (0) 2023.12.26
[백준] 10828  (0) 2023.11.20

+ Recent posts