[코테]11-브루트 포스(비트마스크)
비트 연산
- 비트는 0과 1 밖에 없음, &(and), |(or) , ~(not) , ^(xor)
A=27=11011
A=83=1010011
- 
not연산의 경우에는 자료형에 다라 결과가 달라진다. 
- 
또한 unsigned, signed에 따라서 보여지는 값은 다르다. 
- 
shift left(<<) 
- 
shift right(>>) 
- 
A<<B (A를 왼쪽으로 B비트만큼 민다) 
ㅋㅋㅋㅋ이거 학교 다닐 때 배웠던거네,, 기억이 새록새록ㅜ
- A<<B는 A * 2^B와 같다
- A>>B는 A / 2^B와 같다.
- (A+B) / 2 는 (A+B)>>1로 쓸 수 있다.
비트마스크
- 
정수로 집합을 나타낼 수 있다. 
 { 1, 3, 4, 5, 9 } = 2^1 + 2^3 + 2^4 + 2^5 + 2^9
 -> 다섯개의 집합이 있다고 가정.
- 
집합은 중복되는 수가 없다. 없으면 0 있으면 1로 봄. 
- 
100011010(2) 으로 표현하는 것. 
- 장점
1) 공간을 적게 사용한다. -> 정수 1개를 이용해서 표현할 수 있음.
2) 정수
3) 복잡도가 O(1) - 검사, 추가, 삭제
만약에 {1,100} = 2^100 + 2^1 이 되면 2^100의 숫자가 너무 크니까 잘 사용 안함.
보통 0부터 N-1까지 정수로 이루어진 집합을 나타낼 때 사용.
1부터 N까지 정수로 이루어진 집합을 사용하는 건 공간이 2배 더 필요하다.
또, 각종 연산을 조금 변형해서 사용해야 한다.
따라서, 0부터 N-1까지로 변형해서 사용하는게 더 좋음.
ex) {1,3,4,5,9} = 570
- 
2가 포함되어 있는지 검사 
 해당 숫자를 이진수로 바꾸고 1<<2랑 and연산을 해서 결과값을 보면됨.
- 
비트마스크 S에 X가 있는지 검사? 
 S & (1<<X) = 0이면 없음, 0이 아니고 그 (1<<X)의 값이 나오면 있음.
또 다른 방법,
- ( S >> X ) & 1 이 0인지 1인지 검사해줌
- S >> X의 0번째 비트 = S의 X번째 비트.
- 추가하는 연산
 S에 X를 추가 = S의 X번째 비트를 1로 변경
 OR연산을 사용해주면 됨
- S | (1<<X)
- 제거하는 연산
 S에서 X를 제거 = 해당 비트를 0으로 만들어주어야 함.
- 3을 제거하려면
 그 지우려는 부분만 0으로 만들고 나머지는 다 1로 하고 & 연산을 수행함.
- 없는 수를 지워도 마찬가지로 0이 나옴 왜냐면 원래 0이였으니까 (AND연산이기 때문)
S & ~(1<<X)
토글연산 (0->1) (1->0)
이것도 논리회로 전공수업때 들었던 것,, 기억이 새록새록
- N개의 수를 저장 : 0~ N-1까지
- 전체 집합
 (1 << N) -1
- 공집합
 0
현재 집합이 S일 때
- i를 추가
S | ( 1 << i )- i를 검사
S & ( 1 << i )- i를 제거
S & ~( 1 << i )- i를 토글
S ^ ( 1 << i )
- 비트연산을 사용할 때는 연산자 우선 순위를 생각해야함.
- 1 << N -1 은 (1<<N)-1일까? 아니면 1<<(N-1)일까?
- 정답은 1<<(N-1)임.
- 괄호로 원하는 연산을 묶어주는게 좋은 방법임.
집합

- 1~20까지 사용하는 것으로 되어있는데 0~19로 사용해주어야함.
- 물론 배열을 사용하는 것이 더욱 편리하지만, 비트마스크를 사용하는 이유는 집합을 배열의 인덱스로 표현할 수 있기 때문이다.
- 상태 다이나믹을 할 때 자주 사용하게 된다.
- 재귀 , 브루트 포스 둘다 사용가능.
- 단점. 정수.. (장점도 정수라며..)
=> 정수는 32비트, 64비트 주로 사용. 32비트는 0~31 / 64비트에는 0~63까지만 저장.
적은 개수의 집합에서만 사용하는 것이 좋음.
또 언제 사용?
- 함수 ( 옵션 1, 옵션 2, 옵션 3)이 있을 때 나중에 옵션 4, 옵션 5, 옵션 6을 추가해야함.
 근데 이 때 옵션을 비트마스크를 이용하면 별도의 처리 없이 추가하면 됨.
- 집합 문제는 완전 비트마스크 기본문제인 것 같다..
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringBuilder sb = new StringBuilder();
		
		int m= Integer.parseInt(br.readLine());
		int s= 0;
		
		while(m-->0) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			String op= st.nextToken();
			int num;
			
			switch(op) {
            //여기서 num에 -1을 해주는 것은 아까 위에 설명했던 것임.
            // 0~19
			case "add" :
				num=Integer.parseInt(st.nextToken())-1;
				s |= (1<<num);
				break;
			case "remove" :
				num=Integer.parseInt(st.nextToken())-1;
				s = s & ~(1<<num);
				break;
			case "check":
				num=Integer.parseInt(st.nextToken())-1;
				if((s & (1<<num))!= 0) {
					sb.append("1\n");
				}else {
					sb.append("0\n");
				}
				break;
			case "toggle":
				num=Integer.parseInt(st.nextToken())-1;
				s ^= (1<< num);
				break;
			case "all":
				s |= (~0);
				break;
			case "empty":
				s &= 0;
				break;
			}
		}
		System.out.println(sb);
		
	}
}
부분수열의 합

서로 다른 N개의 정수로 이루어진 수열이 있을 때, 크기가 양수인 부분수열 중에서 그 수열의 원소를 다 더한 값이 S가 되는 경우의 수를 구하는 문제 (1<=N<=20)
- 
N개의 정수로 이루어진 부분수열의 개수를 구하면 됨. 
- 
부분 수열의 개수 = 2^N-1 
- 
각 부분 수열을 만들고 합을 만듬. 
- 
(2^N) * N 
- 
전체 집합 = (1<<N)-1 
- 
크기가 0인 부분수열은 제외. 
- 
집합에 무엇이 포함되어 있는지 확인 
for(int i=1;i< (1<<n); i++){
for(int k=0; k<n; k++){
if( i&(1<<k) )
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
	public static void main(String[] args)  {
		Scanner sc = new Scanner(System.in);
		int n= sc.nextInt();
		int s = sc.nextInt();
		int a[]= new int[n];
		
		for(int i=0;i<n;i++) {
			a[i]=sc.nextInt();
		}
		int res=0;
		for(int i=1;i< (1<<n);i++) {
			int sum=0;
			for(int j=0;j<n;j++) {
				if( (i&(1<<j)) !=0 )
					sum += a[j];
			}
			if(sum==s) {
				res+=1;
			}
		}
		System.out.println(res);
	}
}스타트와 링크
~~이거 예전에 풀었던 것임,,, ~~

종이 조각

N * M크기의 종이를 조각으로 잘라서 합의 최대값을 구하는 문제(1<=N, M<=4)
- 가장 먼저 생각해보아야 하는 것은 ?
 어떻게 종이를 조각으로 자르는 것이 합이 최대가 될 것인가,,
 4개의 조각으로 나누는게 제일 클 것임.
 근데 0이 있을 때를 생각해야함.
- 브루트 포스로 가능한 모든 방법으로 종이 조각을 나누어 보아야 함.
- 2개로 나누어 지는 것이 가로조각과 세로조각임.
- 가로는 0, 세로는 1로 표현한다면 0과 1로 이루어진 NM개의 칸으로 볼 수 있음.
- NM= 44= 16자리의 비트마스크로 이용하면 됨.
어떤 수를 나누었을 때 ? 원래 수보다 더 커지는 경우는 없음.
각각의 칸에서 가로인지 세로인지 정하면 됨.
1) NM자리의 비트마스크. 모든 방법의 수를 만들어봄
i * m + j를 이용.
2) 수의 합을 구해주면 됨.
각각의 행에 대해서 얼마나 연속되는지, 각각의 열에 대해서 얼마나 연속되는지를 구함.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
	public static void main(String[] args)  {
		Scanner sc = new Scanner(System.in);
		int n= sc.nextInt();
		int m= sc.nextInt();
		int a[][]=new int[n][m];
		
		for(int i=0;i<n;i++) {
			String str= sc.next();
			for(int j=0;j<m;j++) {
				a[i][j] = str.charAt(j)-'0';
				//문자인 숫자를 원래 숫자로 바꾸려면 문자0을 빼주면 됨.
			}
		}
		
		int res=0;
		
		for(int s=0;s< (1<<(n*m));s++) {
			int sum=0;
			for(int i=0;i<n;i++) {
				int cal=0;
				for(int j=0;j<m;j++) {
					int k=i*m +j;
					if( (s&(1<<k))==0 ) {
						cal = cal * 10 + a[i][j];
						//기존의 숫자 32에 5를 추가하면 32*10 + 5 이런식으로 함.
					}else {
						sum += cal;
						cal =0;
					}
				}
				sum += cal;
			}
			
			for(int j=0;j<m;j++) {
				int cal=0;
				for(int i=0;i<n;i++) {
					int k= i*m+j;
					if( (s&(1<<k))!=0 ) {
						cal = cal*10 + a[i][j];
					}else {
						sum+= cal;
						cal=0;
					}
				}
				sum += cal;
			}
			res = Math.max(res, sum);
		}
		System.out.println(res);
		
	}
}
최백준 선생님의 코딩테스트 기초 강의를 보고 작성한 글입니다.
Author And Source
이 문제에 관하여([코테]11-브루트 포스(비트마스크)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@tms01365/코테11-브루트-포스비트마스크저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)