#1081 주사위를 2개 던지면?
1부터 n까지의 출력은 for문으로 해결 가능하지만, 1부터 n까지, 1부터 m까지의 조합은 이중 for문을 사용해야 한다.
이중 for문은 바깥에 있는 for문이 1회 실행되고 안에 있는 for문이 n회 실행된다. 그 후 바깥의 for문이 2회째 실행이 되고 안에 있는 for문이 n회 실행된다. 이를 이용하면 구구단, 두 가지 경우의 모든 경우의 수 등에 사용할 수 있다. 여러개의 for문을 중첩해서 사용할 땐 가급적 서로 다른 변수를 사용해서 서로의 for문이 실행되는데 지장이 없도록 하는게 좋다.
#include <stdio.h>
int main(){
int n,m;
int i,j;
scanf("%d %d", &n, &m);
for(i=1; i<=n; i++){
for(j=1; j<=m; j++){
printf("%d %d \n", i, j);
}
}
return 0;
}
#1082 16진수 구구단?
10진수의 구구단을 생각해보면, 예를 들어 3단이라고 했을 때,
3*1=3
3*2=6
3*3=9
3*4=12
.
.
.
3*9=27
이면. 여기서 앞의 3은 고정이고 두번째 숫자는 1부터 9까지 순차적으로 증가하고 세번째 숫자는 앞의 두 숫자를 곱한 것이다.
이를 for문을 이용한다면
for(i=1; i<=9; i++){
printf("3*%d=%d", i, 3*i);
}
와 같은 형태로 짤 수 있다. 다만, 이건 10진수이기 때문에 i의 범위가 1~9이지만, 16진수라면 1~15로 범위를 잡아주고 출력 또한 %d가 아닌 %x 혹은 %X로 해주면 된다. 입력한 단에 대해 출력하고 싶다면 3 대신 입력받은 변수를 넣어주면 된다.
출력 예시에서 알파벳이 대문자로 출력되었기 때문에 %X를 사용한다.
#include <stdio.h>
int main(){
int n, i;
scanf("%X", &n);
for(i=1; i<16; i++){
printf("%X*%X=%X\n", n, i, n*i);
}
return 0;
}
#1083 3 6 9 게임의 왕이 되자!
for문 안에 if문을 통해 i가 3의 배수인지 확인해주면 된다.
#include <stdio.h>
int main(){
int n,i;
scanf("%d", &n);
for(i=1; i<=n; i++){
if(i%3)
printf("%d ", i);
else
printf("X ");
}
return 0;
}
#1084 빛 섞어 색 만들기
3중 for문도 원리는 2중 for문과 같다.
이 때, count라는 정수형 변수를 선언하고 0으로 초기화한 후에 제일 내부에 있는 for문에 count++;을 한줄 입력해주고 프로그램이 종료되기 전에 count를 출력하면 해당 for문이 총 몇번 실행되었는지 알 수 있다.
#include <stdio.h>
int main(){
int r, g, b, i, j, k, cnt=0;
scanf("%d %d %d", &r, &g, &b);
for(i=0; i<r; i++){
for(j=0; j<g; j++){
for(k=0; k<b; k++){
printf("%d %d %d\n", i, j, k);
cnt++;
}
}
}
printf("%d", cnt);
return 0;
}
#1085 소리 파일 저장용량 계산하기
h, b, c, s를 모두 곱한 값의 단위는 bit이다. 해당 값을 8로 나누면 Byte, 그것을 1024(2^10)로 나누면 KB, 그것을 1024(2^10)로 나누면 MB가 된다. 이 때의 값은 소수가 나올 수 있기 때문에 실수형 변수를 선언한다. 처음 작성한 코드는 다음과 같다.
#include <stdio.h>
int main(){
int h, b, c, s;
double result=0;
scanf("%d %d %d %d", &h, &b, &c, &s);
result = (double)(h * b * c * s) / 8 / 1024 / 1024;
printf("%.1lf MB", result);
return 0;
}
입력이 48000, 32, 5, 300일 때 274.7 MB가 출력되어야 하는데 처음 작성한 코드에선 -237.3 MB가 나왔다. 부호가 바뀌어서 나온 것을 보니 오버플로우의 문제인가 싶어 일단 48000*32*5*300을 직접 계산해 보았더니 2,304,000,000이 나왔다. 처음엔 int 대신 long int를 사용할까 싶었지만 입력 값은 음수일 수가 없기 때문에 long int 대신 unsigned int가 적절할 것 같아 수정하였더니 해결되었다.
#include <stdio.h>
int main(){
unsigned int h, b, c, s;
double result=0;
scanf("%d %d %d %d", &h, &b, &c, &s);
result = (double)(h * b * c * s) / 8 / 1024 / 1024;
printf("%.1lf MB", result);
return 0;
}
오버플로우에 대하여
지금까지는 값을 계산하는 과정에 상관없이 값이 변수에 저장되는 순간에만 자료형의 범위와 값의 범위가 같으면 오버플로우가 발생하지 않는 줄로만 알았다. 하지만 이번의 경우엔, 값을 저장하는 순간은 문제가 없었지만 계산되는 과정에서 오버플로우가 일어났기 때문에 의문이 들어 찾아보았다. 이해하려면 어셈블리어에 대해 알아야 하는데, 찾아본 바에 의하면, 연산이 이루어지면 해당 값은 eax라는 레지스터에 저장되는데 이 eax레지스터가 32bit이기 때문에 eax에서 오버플로우가 일어난다는 것이다. 그렇다면 long int의 크기는 8byte(64bit)인데 어떻게 eax에서 오버플로우가 일어나지 않고 정확한 값을 반환하는지 궁금해서 알아봤더니, eax를 포함하는 rax라는 더 큰 레지스터가 존재한다는 것이다. rax는 64비트의 공간을 가지고 있는데, long int, long long int와 같이 선언하면 해당 값은 eax를 넘어서 rax 전체가 사용되기 때문에 더 큰 값을 저장할 수 있는 것이었다. 반대로 short와 같이 int보다 크기가 작은 자료형도 eax에 저장되는 것이라면 이는 왜 int의 범위만큼 표현할 수 없고 중간에 오버플로우가 일어나는지에 대해서도 찾아보았더니, eax를 또 반으로 나누어서 후반부 16비트를 ax로써 일부만 사용할 수 있는 구조란다. 그러니 short가 선언되면 ax만큼만 할당되기 때문에 그만큼 더 범위가 작아지는 것이다.
구조를 이미지화 하면 다음과 같다.
#1086 그림 파일 저장용량 계산하기
그림파일의 용량은 가로 픽셀 수 * 세로 픽셀 수(해상도) * 픽셀 당 비트 수(색상에 대한 값, 보통은 rgb각각 8비트씩 3개로 24)로 계산하며 단위는 bit이다.
#include <stdio.h>
int main(){
unsigned int w, h, b;
double result=0;
scanf("%d %d %d", &w, &h, &b);
result = (float)w * h * b / 8 / 1024 / 1024;
printf("%.2lf MB", result);
return 0;
}
#1087 여기까지! 이제 그만~
for문을 순회하며 sum에 i를 더해준다. sum이 n보다 커지면 break한 후에 sum을 출력한다.
#include <stdio.h>
int main(){
int n, i, sum=0;
scanf("%d", &n);
for(i=0; ;i++){
sum+=i;
if(sum>=n)
break;
}
printf("%d", sum);
return 0;
}
#1088 3의 배수는 통과?
break와 달리 continue 라는 명령문이 있다. continue를 만나는 즉시 아래의 내용들을 모두 건너뛰고 다음 반복을 수행한다.
i가 3의 배수일 때 continue를 실행시켜주면 출력문을 실행하지 않고 다음 과정으로 넘어가게 된다.
#include <stdio.h>
int main(){
int n,i;
scanf("%d", &n);
for(i=1; i<=n; i++){
if(i%3==0)
continue;
printf("%d ", i);
}
return 0;
}
#1089 수 나열하기1
등차수열의 일반항은
위와 같다. 시작값, 등차, 몇번째 항인지에 대한 입력을 각각 a1, d, n에 대입한 후 계산해주면 된다.
#include <stdio.h>
int main(){
int a, d, n;
scanf("%d %d %d", &a, &d, &n);
printf("%d", a + (n-1) * d);
return 0;
}
#1090 수 나열하기2
등비수열의 일반항은
위와 같다. 입력받은 값을 각각 a1, r, n에 대입하여 계산한 결과를 출력하면 된다. 이 때, 제곱을 계산하는 방법으로 C언어에서 제공하는 수학관련 라이브러리 math.h에 정의된 pow() 함수를 이용한다.
pow(a, b) -> a^b 반환. pow함수의 인자와 반환 값은 모두 double이지만, 정수는 실수의 안에 포함되는 수이므로 정수의 형태로 입력해도 무방하다. 다만, a*pow(r, n-1)은 정수 * 실수의 형태이므로 계산 값이 실수가 되기 때문에, pow(r, n-1)의 결과값을 int형으로 타입캐스팅 해주어야 한다.
주어진 예시에서의 최대값은 10, 10, 10 으로 결과가 100억이 된다. long int를 사용하면 된다.
#include <stdio.h>
#include <math.h>
int main(){
long int a, r, n;
scanf("%ld %ld %ld", &a, &r, &n);
printf("%ld", a*(int)pow(r, n-1));
return 0;
}
#1091 수 나열하기3
해당 문제는 일반항을 구하기가 어렵기 때문에 for문을 이용하여 작성하였다. a를 1로 초기화 선언후에, n번째 항이 될때까지 a=a*m+d을 반복하였다. 값의 크기가 매우 커지기 때문에 long long int를 사용하였다.
#include <stdio.h>
int main(){
long long int a=1, m, d, n, i;
scanf("%ld %ld %ld %ld", &a, &m, &d, &n);
for(i=2; i<=n; i++){
a = a*m + d;
}
printf("%ld", a);
return 0;
}
#1092 함께 문제 푸는 날
해당 문제는 수학에서의 최소공배수의 개념으로 해결 할 수 있지만, 생각을 조금 다르게 하면 다른 풀이가 있다. 만약 3과 7의 최소공배수를 구한다면 두 수를 곱한 21이 되겠지만, 4 6의 최소공배수는 24가 아닌 12가 된다. 이러한 결과를 어떻게 출력해야 할까. 다른 좋은 풀이가 있는진 모르겠지만 1부터 1씩 더해가면서 최소공배수를 구하고자 하는 각각의 수와 나눈 나머지가 모두 0이 되는 처음의 숫자가 최소공배수가 될 것이다. 아래 코드는 그러한 과정을 담았다.
#include <stdio.h>
int main(){
int a, b, c, day=2;
scanf("%d %d %d", &a, &b, &c);
while(day%a || day%b || day%c){
day++;
}
printf("%d", day);
return 0;
}
#1093 이상한 출석 번호 부르기1
배열을 활용해서 해결할 수 있다. 만약 1~100까지의 숫자가 각각 몇번 입력되었는지 확인하고 싶다면, 먼저 1~100에 상응하는 배열을 선언하고 모두 0으로 초기화한다. 배열을 한번에 초기화해주는 방법은 선언과 동시에 초기화하는 방법인데, arr[100]={0, }과 같이 작성하면 모든 인덱스가 0으로 초기화된다. 그 후에 값이 입력되면 해당 값의 인덱스를 ++해준다. 예를 들어 n에 1이 입력되었다면 arr[n]++로 입력된 횟수를 누적시킬 수 있다. 이 때, 숫자는 1~100이지만 100개 짜리 배열의 인덱스는 0~99이므로, 저장할때 arr[n-1]++과 같이 해주거나, 아니면 처음부터 101개짜리 배열을 선언해서 arr[0]은 버리고 arr[1]부터 arr[100]까지 사용하는 법이 있지만 이는 비교적 비효율적이므로 첫 번째의 방법으로 작성하였다. 총 숫자는 1~23개 이므로 arr[23]으로 선언해주면 된다.
#include <stdio.h>
int main(){
int n, x, i;
int arr[23]={0,};
scanf("%d", &n);
for(i=0; i<n; i++){
scanf("%d", &x);
arr[x-1]++;
}
for(i=0; i<23; i++){
printf("%d ", arr[i]);
}
return 0;
}
#1094 이상한 출석 번호 부르기2
이번엔 불린 횟수가 아니라 불린 번호를 그대로 저장한 후에 for문의 초기식을 n-1, 조건식을 0보다 크거나 같을 때, 증감식을 i--로 작성하면 된다. 만약 부른 횟수에 상관없이 23개의 숫자를 출력해야 한다면 초기식은 i=22와 같이 되겠지만, 출력 예시를 보면 입력한 만큼만 출력하기 때문에 초기식을 i=n-1으로 둔다. 만약 n이 10이라면 0~9까지 저장되었기 때문에 초기식을 10이 아닌 9로 두어야 한다.
#include <stdio.h>
int main(){
int n, i;
int arr[10000]={0,};
scanf("%d", &n);
for(i=0; i<n; i++){
scanf("%d", &arr[i]);
}
for(i=n-1; i>=0; i--){
printf("%d ", arr[i]);
}
return 0;
}
#1095 이상한 출석 번호 부르기3
해당 문제의 풀이 방법은 다음과 같다.
1. for문을 통해 입력한 수만큼 반복해서 입력을 받는다.
2. 입력된 수와 arr[0]을 비교하여 더 큰 값을 arr[j]에 넣고 j를 1 증가시킨다.(배열에 숫자를 쌓아가기 위해서)
3. 입력된 수와 arr[0]을 비교하여 더 작은 값을 arr[0]에 넣는다.
이러한 과정을 n번 거치고 나면, arr[0]에는 제일 작은 값이 저장되게 된다.
#include <stdio.h>
int main(){
int arr[10000]={30,}, i, j=1, n, input;
scanf("%d", &n);
for(i=0;i<n; i++){
scanf("%d", &input);
if(input < arr[0]){
arr[j++] = arr[0];
arr[0] = input;
}
else
arr[j++] = input;
}
printf("%d", arr[0]);
return 0;
}
#1096 바둑판에 흰 돌 놓기
2차원 배열은 1차원 배열을 여러 줄 나열한 형태이다. 1차원 배열을 사용하다보면 for문과 궁합이 굉장히 잘 맞는데, 2차원 배열은 2중 for문을 이용하면 손쉽게 관리할 수 있다.
#include <stdio.h>
int main(){
int i, j, x, y, n;
int arr[19][19] = {0, };
scanf("%d", &n);
while(n--){
scanf("%d %d", &x, &y);
arr[x-1][y-1] = 1;
}
for(i=0; i<19; i++){
for(j=0; j<19; j++){
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
#1097 바둑알 십자 뒤집기
2차원 배열의 한 인덱스 arr[n][n]에서 왼쪽, 오른쪽, 위, 아래 인덱스에 접근하는 방법은 각각 arr[n][n-1], arr[n][n+1], arr[n-1][n], arr[n+1][n]이 된다. 좌표 x, y가 입력되면, arr[x][0]~ arr[x][19], arr[0][y] ~ arr[19][y]의 값을 for문을 이용해서 0은 1로, 1은 0으로 바꿔준다.
#include <stdio.h>
int main(){
int i,j, x,y, n;
int arr[19][19];
for(i=0; i<19; i++){
for (j=0; j<19; j++){
scanf("%d", &arr[i][j]);
}
}
scanf("%d", &n);
while(n--){
scanf("%d %d", &x, &y);
for(i=0; i<19; i++){
arr[x-1][i] = !arr[x-1][i];
arr[i][y-1] = !arr[i][y-1];
}
}
for(i=0; i<19; i++){
for(j=0; j<19; j++){
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
#1098 설탕과자 뽑기
과자를 놓는 시작점 arr[x][y]부터 d가 0이면 y를 1씩, d가 1이면 x를 1씩 막대의 길이만큼 증가시키며 1을 입력한다. 배열의 인덱스는 항상 0부터 시작하기 때문에 x, y가 아닌 x-1, y-1로 표기하는 것을 주의해야 한다.
#include <stdio.h>
int main(){
int w, h, l, d, arr[100][100]={0,};
int i, j, n, x, y;
scanf("%d %d", &w, &h); //격자판의 크기
scanf("%d", &n); //입력 횟수.
while(n--){
scanf("%d %d %d %d", &l, &d, &x, &y); //길이, 방향, 시작점의 x좌표, 시작점의 y좌표.
if(d==0){
for(i=0; i<l; i++){
arr[x-1][y-1+i] = !arr[x-1][y-1+i];
}
}
else if(d==1){
for(i=0; i<l; i++){
arr[x-1+i][y-1] = !arr[x-1+i][y-1];
}
}
}
for(i=0; i<w; i++){
for(j=0; j<h; j++){
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
#1099 성실한 개미
오른쪽으로 움직일 수 있는지 확인하는 것이 첫 번째, 움직일 수 없다면 아래로 움직일 수 있는지 확인하는 것이 두 번째, 아래로도 움직일 수 없다면 종료하는 것이 세 번째다.
while문으로 먹이를 찾거나 끝에 도착할 때까지 반복한다. 먼저 오른쪽으로 움직일 수 있는지에 대해 검사해야 한다. 오른쪽 칸의 숫자는 0, 1, 2 3가지 중 하나이기 때문에 각각 if문을 사용하여 그때마다의 동작을 작성한다.
오른쪽이 0 -> 오른쪽으로 한 칸 이동해주면 된다.
오른쪽이 1 -> 아래로 한 칸 이동해야 한다. 이 때, 아래의 숫자가 무엇인지 알 수 없기 때문에 아래 숫자가 0, 1, 2일때 각각의 동작을 작성해주어야 한다.
오른쪽이 2 -> 오른쪽으로 한 칸 이동시킨 후, 해당 인덱스의 값을 9로 바꿔준 후에 break 한다.
오른쪽이 1일 때,
아래가 0 -> 아래로 한 칸 이동해주기만 하면 된다.
아래가 1 -> 오른쪽, 아래가 모두 막혀있기 때문에 그대로 break 하면 된다.
아래가 2 -> 아래로 한 칸 이동시킨 후, 해당 인덱스의 값을 9로 바꿔준 후에 break 한다.
처음에는 while문을 무한루프로 작성하고 while문 바로 전의 if문을 작성하지 않고 제출하였더니, 시작 지점에 먹이가 있는 경우를 확인하지 못했다. 그래서 방법을 생각하다가, while문 내의 시작점에 if(arr[x][y]==2) 조건을 걸었다. 그러고나서 곰곰히 생각해보니, 어차피 시작점을 검사하기 위해서 작성하는 코드인데, while문이 반복되는 동안 if문에서 조건검사를 계속 한다고 생각하니 뭔가 찝찝했다. 그래서 변수를 하나 더 사용해서 while이 시작되기 전에 시작점을 검사하고, 시작점에 먹이가 있으면 flag를 0으로 만들어주어서 while문이 실행되지 않도록 하는 방식으로 작성하였다. 코드는 아래와 같다.
#include <stdio.h>
int main(){
int arr[10][10];
int i, j;
int flag=1;
int x=1, y=1; //시작 위치. (2, 2)이지만 인덱스는 0부터 시작하기 때문에 (1, 1)로 사용.
for(i=0; i<10; i++){
for(j=0; j<10; j++){
scanf("%d", &arr[i][j]);
}
}
if(arr[x][y]==2){
flag=0;
arr[x][y]=9;
}
while(flag){
if(arr[x][y]==2){
arr[x][y] = 9;
break;
}
arr[x][y] = 9;
if(arr[x][y+1] == 0){
y++;
}
else if(arr[x][y+1] == 1){
if(arr[x+1][y] == 1)
break;
else if(arr[x+1][y] == 0)
x++;
else if(arr[x+1][y] == 2){
x++;
arr[x][y]=9;
break;
}
}
else if(arr[x][y+1] == 2){
y++;
arr[x][y] = 9;
break;
}
}
for(i=0; i<10; i++){
for(j=0; j<10; j++){
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
'Programming > C' 카테고리의 다른 글
[ProjectH4C] 코딩도장 Unit 20 ~ 22 Write-Up (0) | 2020.07.14 |
---|---|
[ProjectH4C] 코딩도장 Unit 17 ~ 19 Write-Up (0) | 2020.07.13 |
[ProjectH4C] 코드업(CodeUp) 기초 100제 1061~1080 Write-up (0) | 2020.07.09 |
[ProjectH4C] 코드업(CodeUp) 기초 100제 1041~1060 Write-up (0) | 2020.07.07 |
[ProjectH4C] 코드업(CodeUp) 기초 100제 1021~1040 Write-up (0) | 2020.07.07 |