[ 알고리즘풀이 ]

 DP 문제로 DP[N] : 2 x N 타일링하는 경우의 수라고 한다면, 2 x N 타일링은 2 x (N - 1) 타일링 방법에 세로 한 줄을 추가하는 경우의 수와 2 X (N - 2) 타일링 방법에 가로 두 줄을 추가하는 경우의 수의 합으로 다음과 같은 점화식이 성립합니다.

■ DP[N] = DP[N - 1] + DP[N - 2]

 이때, 여러개의 테스트 케이스에 대해서 2 x N 타일링하는 경우의 수를 출력하는 게 아니라 단 한 번만 출력하면 되므로 굳이 배열을 선언해 메모리를 낭비하지 않고 2 x N 타일링하는 경우의 수를 앞에서부터 차곡차곡 쌓아가면 됩니다.

#include <string>
#include <vector>
#define M 1000000007
using namespace std;

int solution(int n) {
    int answer = 0, left = 0, right =1, temp;
    for(int i = 2; i <= n; i++){
        temp = (left + right) % M;
        left = right;
        right = temp;
    }
    answer = (left + right) % M;
    return answer;
}

 

안녕하세요, 여행벌입니다.

오늘은 자바 연산자 중 이항 연산자들에 대해서 다뤄보겠습니다.


자바 연산자 종류


연산기호

결합 방향

우선순위 ( 낮을수록 높음 )

[ ], ( ), .

expr++, expr--

2

++expr, --expr, +expr, -expr, ~, !, (type), new

3

*, /, %

4

+, -

5

<<, >>, >>>

6

<, >, <=, >=, instanceof

7

==, !=

8

&

9

^

10

|

11

&&

12

||

13

? expr : expr

14

=, +=, -=, *=, /=, %=, ^=, |=, <<=, >>=, >>>=

15


이항 연산자


 피연산자가 둘인 연산자를 가리켜 '이항 연산자(binray operator)' 라 합니다. 피연산자는 데이터 그 자체를 대표함과 동시에 조작, 연산에 사용할 데이터를 지정하는 컴퓨터 명령의 일부입니다.


대입 연산자와 산술 연산자 : =, +, -, *, /, %


연산자 연산자의 기능 결합 방향
=

연산자 오른쪽에 있는 값을 연산자 왼쪽에 있는 변수에 대입.

a = 5;

+

두 연산자의 값을 더한다.

a = 4 + 3;

-

왼쪽의 피연산자 값에서 오른쪽의 피연산자 값을 뺀다.

a = 4 - 3;

*

두 피연산자의 값을 곱한다.

a = 4 * 3;

 

/

왼쪽의 피연산자 값을 오른쪽의 피연산자 값으로 나눈 몫을 반환한다.

a = 4 / 3;

%

왼쪽의 피연산자 값을 오른쪽의 피연산자 값으로 나눴을 때

얻게 되는 나머지를 반환한다.

a = 4 % 3;

 


복합 대입 연산자 : +=, -=, *=, /=, %=, &=, ^=, <<=, >>=, >>>=


연산자 연산자의 기능
+= a += b 는 a = a + b; 와 같습니다
-= a -= b 는 a = a - b; 와 같습니다.
*= a *= b 는 a = a * b; 와 같습니다.
/= a /= b 는 a = a / b; 와 같습니다.
%= a %= b 는 a = a % b; 와 같습니다.
&= a &= b 는 a = a & b; 와 같습니다.
^= a ^= b 는 a = a ^ b; 와 같습니다.
<<= a <<<= b 는 a = a << b; 와 같습니다.
>>= a >>= b 는 a = a >> b; 와 같습니다.
>>>= a >>>= b 는 a = a >>> b; 와 같습니다.

관계 연산자 : <, >, <=, >=, ==, !=


 관계 연산자는 두 개의 피연산자 사이에서 크기 및 동등 관계를 따져주는 이항 연산자로 결과에 따라서 true 또는 false를 반환합니다.

연산자 연산자의 기능 결합 방향
<

a < b

a 가 b 보다 작은가?

>

a > b

a 가 b 보다 큰가?

<=

a <= b

a 가 b 보다 작거나 같은가

>=

a >= b

a 가 b 보다 크거나 같은가

==

a == b

a 가 b 랑 같은가?

!=

a != b

a 가 b랑 다른가?


논리 연산자 : &&, ||, !


연산자 연산자의 기능 결합 방향
&&

A && B

A와 B 모두가 true이면 true, 아니면 false ( 논리 AND 와 동일 )

||

A || B

A와 B 모두가 false이면 false, 아니면 true ( 논리 OR 과 동일 )

!

!A

A가 true이면 false, false이면 true 반환 ( 논리 NOT )


예시 1)


package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		int num1 = 0;
		int num2 = 0;
		boolean result;
		
		result = ((num1 += 10) < 0) && ((num2 += 10) > 0);
		System.out.println(result);
		System.out.println(num1);
		System.out.println(num2);
	}
}

 위의 출력 결과는 어떻게 될까요?

false
10
0

 num1은 10이지만, num2는 0인 것을 확인할 수 있습니다. 그 이유는 && 연산자 때문입니다. ( (num1 += 10) < 0 ) 이 먼저 수행되는데, num1 += 10을 하면 num1은 10이 되므로 0보다 작지 않습니다. 따라서 ( (num1 += 10) < 0 ) 은 false가 됩니다. 이때, && 연산자는 false가 하나라도 존재하면 어차피 false 값을 같습니다. 따라서 ( (num2 += 10) > 0) 연산을 진행하지 않고 result 에는 false가 저장됩니다. 


예시 2)


package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		int a = 5; // 코드 1번
		System.out.println(a++); // 코드 2번
		System.out.println(a); // 코드 3번
		System.out.println((a++ + 5) * 3); // 코드 4번
		a = (a++ + 5) * 3; // 코드 5번
		System.out.println(a); // 코드 6번
	}
}

 위의 코드는 어떤 값들을 출력할까요? 정답은 아래와 같습니다!

5
6
33
36

 코드 1번에서 a 에 5를 할당합니다.

 코드 2번에서는 a를 출력한 후, ++ 연산이 진행되므로 '5'를 출력하지만 a의 값은 6이 됩니다.

 코드 3번은 코드 2번에 의해 '6' 을 출력하게 됩니다.

 코드 4번에는 ( ) , ++, +, * 4가지 연산이 등장합니다. 우선순위는 ( ), ++, *, + 순으로 괄호 안에 식을 먼저 진행해야 됩니다. 따라서 ++ 과 + 연산이 진행되는데 이때, ++ 연산은 명령을 마무리 한 후에 a의 값을 올려주므로 (6 + 5) 가 진행됩니다. 그 후 * 3을 해서 33이라는 값이 만들어지고 a는 1 증가해서 7이 됩니다. 

 코드 5번은 ( ), ++, +, * , = 5가지 연산이 등장합니다. 우선순위는 ( ), ++, *, +, = 순으로 코드 4번과 마찬가지로 괄호 안에 식을 먼저 진행해야됩니다. 따라서 (7 + 5) 가 먼저 계산되고, 12 * 3이 계산되고 ++ 연산에 의해 a는 1 증가해서 8이 됩니다. 하지만 = 연산에 의해 계산된 36이란 값이 a에 저장되므로 a는 결국 36이 저장됩니다.


예시 3)


package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		int a = 1; // 코드1
		a = ~a; // 코드2
		System.out.println(a); // 코드3
	}
}

 위의 예시는 어떤 값을 출력할까요?

-2

 정답은 '-2' 입니다.

 컴퓨터는 모든 숫자를 2진수로 저장합니다. 우리는 앞의 포스팅에서 int 는 4바이트로 이루어져 있다고 배웠습니다. 따라서, a에 1을 할당하면 다음과 같이 컴퓨터에는 저장됩니다.

 a = 0000 0000 0000 0000 0000 0000 0000 0001 (2)

 ~ 연산은 쉽게 말하면 2진수로 저장된 a의 비트들을 반대로 바꿔주는 연산입니다. 즉, 코드2에 의해 a는 다음과 같이 바뀝니다.

 a = 1111 1111 1111 1111 1111 1111 1111 1110 (2)

 컴퓨터가 정수는 저장하는 방법인 2의 보수법에 의하면 위의 2진수 코드는 '-2' 를 의미합니다. 따라서 -2가 출력됩니다.


예시 4) 비트 연산자


 << 연산자는 비트 값들을 왼쪽으로 이동시킨 후에 오른쪽의 빈 공간에는 모두 0으로 채운다.

 >> 연산자는 비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간에 양수라면 0으로, 음수라면 1로 채워줍니다.

 >>> 연산자는 비트 값들을 오른쪽으로 이동시킨 후에 부호와 상관없이 0으로 채워줍니다.

package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		int a = 1; // 코드1
		a = a << 5; // 코드2
		System.out.println(a);
		a = a >> 3; // 코드3
		System.out.println(a);
		
		int b = -2147483648; // 코드4
		b = b >> 1; // 코드5
		System.out.println(b);
		
		int c = -2147483648; // 코드6
		c = c >>> 1; // 코드7
		System.out.println(c);
	}
}

 위의 예시는 어떤 값들을 출력할까요?

32
4
-1073741824
1073741824

 코드1에서 a에 1을 할당하므로 a = 0000 0000 0000 0000 0000 0000 0000 0001 (2) 가 됩니다.

 코드2에 의해 a는 왼쪽으로 5칸씩 비트가 이동하게 되고, 오른쪽의 파란 부분에는 0이 채워지고 위의 노란 부분에 해당하는 비트들은 사라지게 됩니다. 

 a = 0000 0000 0000 0000 0000 0000 0010 0000 (2)

 따라서, 현재 a는 10진법으로 32를 나타냅니다.

 코드3에 의해 a는 오른쪽으로 3칸씩 비트가 이동하게 되고, 양수이므로 왼쪽의 빨간 부분에 0이 채워지게 됩니다.

 a = 0000 0000 0000 0000 0000 0000 0000 0100 (2)

 따라서, 현재 a는 10진법으로 4를 나타냅니다.

 코드4에서 b에 -2147483648 을 할당하므로 b = 1000 0000 0000 0000 0000 0000 0000 0000(2) 가 됩니다.

 코드5에서 >> 연산은 음수 값은 왼쪽의 비는 부분을 1로 채우므로 b = 1100 0000 0000 0000 0000 0000 0000 0000(2) 가 되고, 10진수로 표현하면 -1073741824 가 됩니다.

 코드6에서 c = 1000 0000 0000 0000 0000 0000 0000 0000(2) 가 됩니다.

 코드7에서 >>> 연산은 부호에 상관 없이 왼쪽의 비는 부분을 0으로 채우므로 c = 0100 0000 0000 0000 0000 0000 0000 0000(2) 가 됩니다. 부호를 나타내는 맨 앞 코드가 0이 되어서 c는 양수가 되고 10진수로 표현하면 1073741824가 됩니다.

 

 비트 연산자는 따로 다른 포스팅에서 자세히 다뤄보겠습니다.


 

문제 : https://www.acmicpc.net/problem/8120


[ 알고리즘풀이 ]

 입력으로 들어온 값들을 역으로 순회하며 답을 채워나갈 것이다.

input[x] : x번 째 입력값

check[x] : 1 ~ N 중에서 x 값을 사용했는지 안 했는지 

 현재 input[x] 가 k 라면 아직 사용 안 한 값 중에서 (k + 1) 번째로 큰 값을 선택합니다. 그러면, 역순으로 채우고 있기 때문에 input[x] 보다 값이 크면서 index는 작은 값들이 k 개가 됩니다. 이때, 아직 사용 안 한 값 중에서 (k + 1)번 째로 큰 값이 없다면 불가능한 경우이므로 "NIE"를 출력합니다.

 

ex) input = [ 0 0 1 0 2 0 4 ] 

input 배열을 역으로 순회하며 정답 배열을 채워보자.

1) input[7] = 4 / check = [ 0 0 0 0 0 0 0 ]

check 배열을 순회하며 아직 사용 안 한 값 중에서 5번 째로 큰 값을 선택한다.

→ ans[7] = 3

2) input[6] = 0 / check = [ 0 0 1 0 0 0 0 ]

→ ans[6] = 7

3) input[5] = 2 / check = [ 0 0 1 0 0 0 1 ]

→ ans[5] = 4

4) input[4] = 0 / check = [ 0 0 1 1 0 0 1 ]

→ ans[4] = 6

5) input[3] = 1 / check = [ 0 0 1 1 0 1 1 ]

→ ans[3] = 2

6) input[2] = 0 / check = [ 0 1 1 1 0 1 1 ]

→ ans[2] = 5

7) input[1] = 0 / check = [ 0 1 1 1 1 1 1 ]

→ ans[1] = 1

#include<iostream>

using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int N, input[30001] = {}, ans[30001] = {};
	bool check[30001] = {};

	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> input[i];

	bool flag = false;
	for (int i = N; i >= 1; i--) {
		int c = 0, j = N;
		while (!(c == input[i] && !check[j])) {
			if (!check[j])
				c++;
			j--;
		}
		if (j < 1) {
			flag = true;
			break;
		}
		ans[i] = j;
		check[j] = 1;
	}

	if (flag)
		cout << "NIE\n";
	else{
		for (int i = 1; i <= N; i++)
			cout << ans[i] << '\n';
	}
}

세그트리를 이용해서 푸신 분들이 많은데, 코드를 뜯어보고 추후에 다시 포스팅하도록 하겠습니다.

 안녕하세요, 여행벌입니다.

 오늘은 JAVA의 자동 형 변환(Implicit Conversion) 과 명시적 형 변환(Explicit Conversion) 에 대해서 다뤄보겠습니다.


자료형의 변환


 연산을 진행할 때, 컴파일러는 변수가 자료형이 동일할 것을 기대합니다. 하지만, 다음과 같이 서로 다른 자료형에 대해서 덧셈 연산을 진행한다면 어떻게 될까요?

int num = 5;
long num2 = 10L;
// num + num2 ??

 int 형 변수 num과 long 형 변수 num2 에 대해서 덧셈 연산을 진행한다면 데이터를 다루는 범위가 더 큰 long 형으로 자료형을 통일해줘야 데이터의 손실 없이 연산이 가능할 것입니다.

- num에 저장된 값을 long 형으로 변환하여 메모리에 임시 저장한다.

- 변환된 값과 num2에 저장된 값을 대상으로 덧셈을 진행한다.

따라서, 위의 두 과정이 진행되야만 우리가 원하는 결과를 얻을 수 있을 것입니다.

이러한 과정을 '자료형 변환' 또는 '형 변환' 이라고 합니다.


자동 형 변환(Implicit Conversion)


 프로그래머가 명시한 형 변환이 아니고 필요한 상황에서 규칙에 근거하여 자동으로 일어나는 형 변환을 '자동 형 변환' 이라고 합니다.

- 자료형의 크기가 큰 방향으로 형 변환이 일어난다.

- 자료형의 크기에 상관없이 정수 자료형보다 실수 자료형을 우선시한다.

 위의 두 규칙에 근거하여 자바에서는 자동 형 변환이 발생합니다.

 

 예시를 통해서 조금만 익혀보겠습니다.

int num1 = 50;
long num2 = 9999999999L;
System.out.println(num1 + num2); // num1을 long 형으로 자동 형 변환 후 연산 진행

 자동 형 변환에 의해 에러 없이 다음과 같이 결과가 나옵니다.

[console]
10000000049

 이번에는 정수형과 실수형을 같이 연산해보겠습니다.

package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		int num1 = 50;
		float num2 = 3.04f;
		System.out.println(num1 + num2); // num1을 long 형으로 자동 형 변환 후 연산 진행
	}
}
[console]
53.04

 에러 없이 결과가 나오는 것을 확인할 수 있습니다.


명시적 형 변환(Explicit Conversion)


 자동 형 변환이 진행되지 않는 상황에서도 필요하다면 프로그래머가 '명시적 형 변환' 을 통해서 강제로 형 변환이 이뤄지도록 할 수 있습니다.

double num1 = 3.1415;
int num2 = (int)num1; // num1을 강제로 형 변환 -> num2 = 3

 다음과 같이 변수 앞에 (우리가 원하는 자료형) 을 통해 강제로 형 변환을 진행할 수 있습니다. 위의 예시는 double 형을 int 형으로 강제로 형 변환했으므로 소수점 이하의 값들은 날아가고 num2 에는 3만 저장됩니다.

short num1 = 3;
short num2 = 5;
short result = num1 + num2;

 short 형 num1과 num2를 연산해서 같은 자료형 result에 저장했습니다. 위의 코드는 문제가 없을까요??

 문제가 있습니다!! '자바에서 정수형 연산은 int 형으로 진행한다.' 때문에 num1 + num2의 결과는 int 형입니다. 따라서 다음과 같이 강제로 형 변환을 해야만 에러가 발생하지 않습니다.

package Hello;

public class HelloWorld {
	public static void main(String args[]) {
		short num1 = 3;
		short num2 = 5;
		short result = (short)(num1 + num2);
	}
}

 

+ Recent posts