[문제풀이] 순열2

업데이트:

문제 링크 : 순열2

풀이 시간 : 20분 + 10분

1. 문제 풀이

1부터 N까지 들어있는 배열 2개(A,B)를 각각 순서를 섞고 같은 인덱스 중 큰 값들의 합을 구한다.

그렇게 해서 나온 합 중에서 K 이상인 개수를 구하는 문제

A, B의 조합의 경우를 다 생각하지 않고 A를 고정하고 B가 나올 수 있는 경우만 생각

그 후 A로 만들 수 있는 경우의 수를 곱해주면 정답

1) B의 순서를 섞어서 나올 수 있는 경우의 수를 구한다.(combi 함수)

2) 그 후 K보다 큰 경우의 수들을 구한다.(start 밑 for문)

3) A로 만들 수 있는 경우의 수 곱한다.

2. 소스 코드

    public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		// 메모이제이션을 위한 배열
		ArrayList<Integer>[] arr = new ArrayList[11];
		int[] mux = new int[11];
		int[] sum = new int[11];
		mux[0]=1;
		sum[0]=0;
		// 1부터 N까지의 합과 곱
		for (int i = 1; i < mux.length; i++) {
			mux[i] =mux[i-1]*i;
			sum[i]=sum[i-1]+i;
		}
		int iter = Integer.parseInt(br.readLine());
		for (int i = 1; i <= iter; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			long result = 0;
			int N = Integer.parseInt(st.nextToken());
			int K = Integer.parseInt(st.nextToken());
			// 테스트케이스 중 N이 처음일 때
			if(arr[N] == null) {
				//N일 때 만들수 있는 경우의 수
				//배열이 1부터 N까지 1씩 증가하는 수열이기 때문에
				//N으로 만들 수 있는 최대값-최소값+1이 만들 수 있는 경우의 수
				int size = 0;
				if(N%2==0) {
					size = (sum[N]-sum[N/2])*2-sum[N]+1;
				}else {
					size = (sum[N]-sum[N/2+1])*2+N/2+1-sum[N]+1;
				}
				arr[N] = new ArrayList<Integer>();
				for (int j = 0; j < size; j++) {
					arr[N].add(0);
				}
				//순열 만들기 배열 두 개(A,B)일 떄 A를 {1,2,...N}으로 고정하고 B만 순열 만들기 
				combi(0,new boolean[N],-1*sum[N],arr[N]);
			}
			// K가 최소값보다 작으면 0부터, 크면 K-sum[N]부터
			int start  = K-sum[N] <0 ? 0: K-sum[N];
			for (int j =start; j < arr[N].size(); j++) {
				result += arr[N].get(j);
			}
			// A의 순열의 개수를 곱해주면 총 개수
			bw.write("#"+i+" "+result*mux[N]);
			bw.newLine();
		}
		bw.flush();
		bw.close();
	}
	//배열의 인덱스 참조를 위해서 sum은 N으로 만들 수 있는 최소값*-1로 계산
	static void combi(int cnt, boolean[] v, int sum, ArrayList<Integer> hm) {
		if (v.length == cnt) {
			hm.set(sum, hm.get(sum)+1);
		} else {
			for (int i = 0; i < v.length; i++) {
				if (!v[i]) {
					v[i] = true;
					//B[i]와 A[cnt] 중 큰 값을 골라 더해준다.
					combi(cnt + 1, v, sum +Math.max(i+1,cnt+1), hm);
					v[i] = false;
				}
			}
		}
	}

3. 후기

추석 연휴에 쉬면서 다른 작업을 하다보니 문제 풀이를 못했다.

프로그래머스 문제는 거의 다 풀었고 이제는 SW Expert나 백준에 비중을 두고 풀 예정이다.

문제에 대한 후기는 SW Expert 사이트는 한번에 여러 테스트케이스를 돌리기 때문에 메모이제이션을 하면 좋은 경우가 많다.

테스트케이스로 iter=2, (N=10, K=60), (N=10, K=66)라면 10에 대한 순열 정보를 (N=10, K=60)일 때 한 번 저장하고 두번째는 배열 참조만 하게끔 만들었다.