PS/BOJ

2143 (두 배열의 합, 중요)

easy16 2022. 5. 5. 20:33

두 배열의 합 : https://www.acmicpc.net/problem/2143

 

 

일반적인 부분배열로 생각하고 풀었는데 답이 크게 나왔다.

 

문제를 다시 확인하니, 연속된 부분합이다.

경우의 수를 놓고 계산을 해보니, 부분합의 개수는 N(N+1)/2 이다.

 

여러문제를 겪어보니 가장 번거로운 점이 부분합의 배열을 만드는 것이다.

인덱스와 for문 만드는 연습이 필요하다.

 

2-point 알고리즘은 이제 특별할 것이 없다.

중복이 나오는 경우만 잘 체크해주면 항상 같은 형태이다.

 

//case 1 부분합을 int array로 구현한 case

//수행속도 약 440ms

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

	static int N, M, T;

	static int[] a, b;
	static int[] aa, bb;
	static int aIdx = 0, bIdx = 0;
	static boolean DBG = false;
	static int answer = 0;

	public static void main(String[] args) throws IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		T = Integer.parseInt(st.nextToken());

		N = Integer.parseInt(br.readLine());
		a = new int[N];
		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < N; i++)
			a[i] = Integer.parseInt(st.nextToken());

		M = Integer.parseInt(br.readLine());
		b = new int[M];

		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < M; i++)
			b[i] = Integer.parseInt(st.nextToken());

		aa = new int[(N * (N + 1)) / 2  ];
		for (int i = 0; i < a.length; i++) {
			int sum = 0;			
			for (int j = i; j < a.length; j++) {				
				sum += a[j];
				if (DBG)
				System.out.printf("[%d %d]sum : %d\n", i,j,sum);
				aa[aIdx++] = sum;
			}
		}
		bb = new int[(M * (M + 1)) / 2  ];
		for (int i = 0; i < b.length ; i++) {
			int sum = 0;			
			for (int j = i; j < b.length; j++) {  
				sum += b[j];
				bb[bIdx++] = sum;
			}
		}

		Arrays.sort(aa);
		Arrays.sort(bb);

		if (DBG)
		{
			System.out.println("aa:");
			for (int v : aa)
				System.out.print(v + " ");
			System.out.println();

			System.out.println("bb:");
			for (int v : bb)
				System.out.print(v + " ");
			System.out.println();
		}

		// aa and bb 의 부분합이므로 aa or bb only는 count하지 않는다.

		int aap = 0;
		int bbp = bb.length - 1;
		long cnt = 0;
		while (aap < aa.length && bbp > -1) {

			long aav = aa[aap];
			long bbv = bb[bbp];
			long sum = aav + bbv;

			if (sum > T) {
				bbp--;
			} else if (sum < T) {
				aap++;
			} else {
				long aaCnt = 0, bbCnt = 0;
				while (aap < aa.length && aav == aa[aap]) {
					aap++;
					aaCnt++;
				}

				while (bbp > -1 && bbv == bb[bbp]) {
					bbp--;
					bbCnt++;
				}
				cnt += aaCnt * bbCnt;
				if (DBG)
				System.out.printf("cnt : %d [ %d %d ]aaCnt: %d  bbCnt %d\n", cnt, aav, bbv, aaCnt, bbCnt);
			}
		}

		System.out.println(cnt);
	}

}

 

 

//case2 : 부분합을 ArrayList로 구현한 case로, 부분합의 length를 신경 쓸 필요 없다는 점이 장점. 

수행시간 약 880ms 

참조 :https://lotuslee.tistory.com/31

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

	static int N, M, T;

	static int[] a, b;
	static boolean DBG = false;
	static int answer = 0;
	static ArrayList<Integer> listA = new ArrayList<>();
	static ArrayList<Integer> listB = new ArrayList<>();
	public static void main(String[] args) throws IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		T = Integer.parseInt(st.nextToken());

		N = Integer.parseInt(br.readLine());
		a = new int[N];
		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < N; i++)
			a[i] = Integer.parseInt(st.nextToken());

		M = Integer.parseInt(br.readLine());
		b = new int[M];

		st = new StringTokenizer(br.readLine());
		for (int i = 0; i < M; i++)
			b[i] = Integer.parseInt(st.nextToken());

		for (int i = 0; i < a.length; i++) {
			int sum = 0;			
			for (int j = i; j < a.length; j++) {				
				sum += a[j];
				if (DBG)
				System.out.printf("[%d %d]sum : %d\n", i,j,sum);
				listA.add(sum);
			}
		}
		for (int i = 0; i < b.length ; i++) {
			int sum = 0;			
			for (int j = i; j < b.length; j++) {  
				sum += b[j];
				listB.add(sum);
			}
		}

		Collections.sort(listA);
		Collections.sort(listB);
		
		
		if (DBG)
		{
			System.out.println("listA:");
			for (int v : listA)
				System.out.print(v + " ");
			System.out.println();

			System.out.println("listB:");
			for (int v : listB)
				System.out.print(v + " ");
			System.out.println();
		}

		// aa and bb 의 부분합이므로 aa or bb only는 count하지 않는다.

		int aap = 0;
		int bbp = listB.size()-1;
		long cnt = 0;
		while (aap < listA.size() && bbp > -1) {

			long aav = listA.get(aap);
			long bbv = listB.get(bbp);
			long sum = aav + bbv;

			if (sum > T) {
				bbp--;
			} else if (sum < T) {
				aap++;
			} else {
				long aaCnt = 0, bbCnt = 0;
				while (aap < listA.size() && aav == listA.get(aap)) {
					aap++;
					aaCnt++;
				}

				while (bbp > -1 && bbv == listB.get(bbp)) {
					bbp--;
					bbCnt++;
				}
				cnt += aaCnt * bbCnt;
				if (DBG)
				System.out.printf("cnt : %d [ %d %d ]aaCnt: %d  bbCnt %d\n", cnt, aav, bbv, aaCnt, bbCnt);
			}
		}

		System.out.println(cnt);
	}

}