https://www.acmicpc.net/problem/4195

 

4195번: 친구 네트워크

첫째 줄에 테스트 케이스의 개수가 주어진다. 각 테스트 케이스의 첫째 줄에는 친구 관계의 수 F가 주어지며, 이 값은 100,000을 넘지 않는다. 다음 F개의 줄에는 친구 관계가 생긴 순서대로 주어진

www.acmicpc.net

코드설명

유니온 파인드에 HashMap을 추가로 사용하여 진행합니다. 

 

 

문제 로직입니다.

이 문제같은경우, 일반적으로 친구들이 Index 형태로 주어지지않고, 문자열 형태의 이름으로 주어졌습니다.

우리는 이 친구들의 이름을 HashMap을 활용하여 이름을 지어주어서 일종의 Index를 지은뒤 unionFind하는 알고리즘에 사용할 것입니다. 

HashMap은 친구들의 Indexing 을 하기 위한 도구입니다.

 

문제에 주어지듯 F개의 줄에 친구관계가 주어집니다.

이때 친구는 2개씩 F개의 줄이 주어졌을때, 모두 이름이 다를경우 2*F의 친구가 있으므로 F*2의 사이즈로 선언합니다.

parent = new int[F*2];
friendCount = new int[F*2];

for(int i=0;i<F*2; i++) {
    parent[i] = i; //본인의 index로 초기화시킵니다. 본인의 집합은 본인을 가리킵니다.
    friendCount[i] = 1; //처음 친구수는 1명 본인입니다.
}
  • parent[i] = i 로 선언
  • friendCount는 항상 처음에는 친구수가 본인 1명

만약에 Fred Barney가 주어졌다고 가정합니다.

맨 처음에 말했듯이 HAshMap을 활용하여 친구들의 번호를 Indexing합니다.

<Fred : 0 >

<Barney : 1>

이렇게 HashMap에 데이터가 들어갑니다.

int friendIdx = 0;
HashMap<String, Integer> hashmap = new HashMap<>(); //<String : 이름, index : (노드번호) >
for(int i=0;i<F;i++) {
    st = new StringTokenizer(br.readLine());
    String friendA = st.nextToken();
    String friendB = st.nextToken();

    if(hashmap.containsKey(friendA) == false) { //만약 처음 만난 친구라면, 즉 친구관계에 한번도 union된적 없을경우 index를 저장하기 위한 처리입니다.
        hashmap.put(friendA, friendIdx++);
    }
    if(hashmap.containsKey(friendB) == false) { //만약 처음 만난 친구라면, 즉 친구관계에 한번도 union된적 없을경우 index를 저장하기 위한 처리입니다.
        hashmap.put(friendB, friendIdx++);
    }
    sb.append(unionParent(hashmap.get(friendA), hashmap.get(friendB))).append("\n");
}

 

그리고 unionParent( 0, 1 )을 통해서 친구가 총 몇명인지 구합니다.

아래에서 a == b 일경우 같은 친구 그룹에 속하는 것을 의미합니다. 이미 구해졌다는 의미입니다.

그러므로 friendCount[a]를 return 하면됩니다.

만약 a != b 일경우에는, 기본적으로 a < b, 즉 인덱스가 작을 수록 부모로 선언하도록 우선순위를 둡니다.

parent[b] = a; ( b가 a를 가리키도록 수정합니다.)

또한 우리는 ( a가 지금까지 총 몇명의 친구를 구했는지 알고싶기에 )

friendCount[a] += friendCount[b] 를 통해서 a 그룹에 b그룹의 친구수를  더해줍니다.

그리고 return friendCount[a]를 내보냅니다. 

a >= b 일경우 위의 로직과 같습니다.

public static int unionParent(int a, int b) {
    a = findParent(a);
    b = findParent(b);

    if( a != b) {
        if(a < b) {
            parent[b] = a;
            friendCount[a] += friendCount[b]; //a번쨰 Index에 친구수의 값을 함께 합쳐줘야합니다.
            return friendCount[a];
        }
        else if( a >= b){ 
            parent[a] = b;
            friendCount[b] += friendCount[a];
            return friendCount[b];
        }
    }
    return friendCount[a];
}

 

틀렸었던 부분은, 아래의 코드입니다.

a와 b가 같은경우에 또한 else를 통해서 parent[a] = b, friendCount[b] += friendCount[a]; return friendCount[b] 로직이 실행되었습니다. 

만약 a == b 인 조건이라면, 더하기 연산을 진행하지 않고 바로 friendCount[a] 를 반환해야합니다.

    public static int unionParent(int a, int b) {
    	a = findParent(a);
    	b = findParent(b);
    	
//		a와 b가 같은경우, 더하기 로직을 진행하면 안됩니다.
//    	if(a < b) {
//    		parent[b] = a;
//    		friendCount[a] += friendCount[b];
//    		return friendCount[a];
//    	}
//    	else {
//    		parent[a] = b;
//    		friendCount[b] += friendCount[a];
//    		return friendCount[b];
//    	}
    	
		if( a != b) {
			if(a < b) {
				parent[b] = a;
				friendCount[a] += friendCount[b]; //a번쨰 Index에 친구수의 값을 함께 합쳐줘야합니다.
				return friendCount[a];
			}
			else if( a >= b){ //사실 로직상 idx는 계속해서 증가하는 형식이므로 이 경우는 없지만 추가합니다. 
				parent[a] = b;
				friendCount[b] += friendCount[a];
				return friendCount[b];
			}
		}
		return friendCount[a];
    	
    }

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {
	
	public static int T, F;
	public static int[] parent; //유니온 집합셋
	public static int[] friendCount; //각 인덱스별 친구의 수 
	public static StringBuilder sb = new StringBuilder();
	public static int answer = 0;
	
	public static int findParent(int x) {
		if( x == parent[x]) return x;
		else return parent[x] = findParent(parent[x]);
	}
	
	public static int unionParent(int a, int b) {
		a = findParent(a);
		b = findParent(b);
		
		if( a != b) {
			if(a < b) {
				parent[b] = a;
				friendCount[a] += friendCount[b]; //a번쨰 Index에 친구수의 값을 함께 합쳐줘야합니다.
				return friendCount[a];
			}
			else if( a >= b){ 
				parent[a] = b;
				friendCount[b] += friendCount[a];
				return friendCount[b];
			}
		}
		return friendCount[a];
		
//		if( a < b ) parent[b] = a;
//		else parent[a] = b;
	}
	
    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());
    	
    	for(int t=0;t<T;t++) {
    		st = new StringTokenizer(br.readLine());
    		F = Integer.parseInt(st.nextToken());
    		parent = new int[F*2];
    		friendCount = new int[F*2];

    		for(int i=0;i<F*2; i++) {
    			parent[i] = i; //본인의 index로 초기화시킵니다. 본인의 집합은 본인을 가리킵니다.
    			friendCount[i] = 1; //처음 친구수는 1명 본인입니다.
    		}
    		
    		int friendIdx = 0;
    		HashMap<String, Integer> hashmap = new HashMap<>(); //<String : 이름, index : (노드번호) >
    		for(int i=0;i<F;i++) {
    			st = new StringTokenizer(br.readLine());
    			String friendA = st.nextToken();
    			String friendB = st.nextToken();
    			
    			if(hashmap.containsKey(friendA) == false) { //만약 처음 만난 친구라면, 즉 친구관계에 한번도 union된적 없을경우 index를 저장하기 위한 처리입니다.
    				hashmap.put(friendA, friendIdx++);
    			}
    			if(hashmap.containsKey(friendB) == false) { //만약 처음 만난 친구라면, 즉 친구관계에 한번도 union된적 없을경우 index를 저장하기 위한 처리입니다.
    				hashmap.put(friendB, friendIdx++);
    			}
    			sb.append(unionParent(hashmap.get(friendA), hashmap.get(friendB))).append("\n");
    		}
    		
    	}
    	System.out.println(sb.toString());
    	
    }
    
    
}

 

+ Recent posts