Plite
전자오락 공방
Plite
전체 방문자
오늘
어제
  • 분류 전체보기 (274)
    • 프로젝트 (18)
      • 완성 프로젝트 (3)
      • 프로젝트 진행 내역 (15)
    • 공부 및 정리 (241)
      • 백준 코드 (222)
      • C++ (8)
      • DirectX (2)
      • Unreal Engine (6)
      • 프로그래밍 패턴 (3)
    • 기타 (12)
      • 기타 주저리 (10)
    • 게임과 취미 (1)
    • 대문 (1)

블로그 메뉴

  • 홈
  • 프로젝트
  • 취미, 일상
  • 백준 프로필

공지사항

  • [Read Me]
  • 제 블로그에 방문하신 것을 환영합니다.

인기 글

태그

  • 조합론
  • 스택
  • 백트래킹
  • 트리
  • SCC
  • 브루트포스
  • 분할정복
  • 문자열
  • 수학
  • C++
  • 큐
  • 누적합
  • 우선순위큐
  • 이분탐색
  • 그래프
  • 유니온 파인드
  • 동적계획법
  • 최소 스패닝 트리
  • 정수론
  • 정렬
  • 트라이
  • 투포인터
  • LCA
  • 기하
  • 위상 정렬
  • 백준
  • KMP
  • UC++
  • 구현
  • 세그먼트 트리

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Plite

전자오락 공방

[C++]백준 - 1707번 문제
공부 및 정리/백준 코드

[C++]백준 - 1707번 문제

2021. 8. 13. 17:11

1707번: 이분 그래프 (acmicpc.net)

 

1707번: 이분 그래프

입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K(2≤K≤5)가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V(1≤V≤20,000)와 간선의 개수

www.acmicpc.net

 

1707번 : 이분 그래프


그래프의 정점의 집합을 둘로 분할하여, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있을 때, 그러한 그래프를 특별히 이분 그래프 (Bipartite Graph) 라 부른다.

그래프가 입력으로 주어졌을 때, 이 그래프가 이분 그래프인지 아닌지 판별하는 프로그램을 작성하시오.

 

입력


입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K(2≤K≤5)가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V(1≤V≤20,000)와 간선의 개수 E(1≤E≤200,000)가 빈 칸을 사이에 두고 순서대로 주어진다. 각 정점에는 1부터 V까지 차례로 번호가 붙어 있다. 이어서 둘째 줄부터 E개의 줄에 걸쳐 간선에 대한 정보가 주어지는데, 각 줄에 인접한 두 정점의 번호가 빈 칸을 사이에 두고 주어진다.

 

 

출력


K개의 줄에 걸쳐 입력으로 주어진 그래프가 이분 그래프이면 YES, 아니면 NO를 순서대로 출력한다.

 

 

 


 

생각해 볼 점


이분 그래프 - 위키백과, 우리 모두의 백과사전 (wikipedia.org)

 

이분 그래프 - 위키백과, 우리 모두의 백과사전

2색변 이분 그래프의 예 그래프 이론에서, 이분 그래프(二分graph, 영어: bipartite graph)란 모든 꼭짓점을 빨강과 파랑으로 색칠하되, 모든 변이 빨강과 파랑 꼭짓점을 포함하도록 색칠할 수 있는 그

ko.wikipedia.org

 

이분 그래프를 만들려면, 노드를 두 그룹으로 나누어야 합니다.

 

Red 그룹과 Blue 그룹으로 나누었다고 생각하고,

 

같은 그룹 사이에 간선이 있으면 안되므로, BFS 혹은 DFS 탐색을 할 때, 다음과 같이 진행합니다.

 

1. 처음 탐색하는 노드를 Red 혹은 Blue로 지정

2. 간선이 이어진 노드를 반대 색상으로 지정함

3. 모든 노드를 탐색할 때까지 반복하되, 간선이 이어진 노드가 같은 그룹인 것을 발견했다면, NO를 출력 

 

 

코드


#include <iostream>
#include <queue>
#include <vector>
using namespace std;

int main() 
{
	int K;
	cin >> K;
	for(int T = 0; T < K; T++)
	{
	    
       	int V, E;
	    cin >> V >> E;
	    
	    //동적 할당
       	vector<int> *graph = new vector<int>[V];
	    int *visited = new int[V];		//방문하지 않음 = 0, Red = 1, Blue = 2
	    fill_n(visited, V, 0);
	    queue<int> q;
	    
	    //입력부
	    for(int i = 0; i < E; i++)
	    {
	        int dep, des;
	        cin >> dep >> des;
	        graph[dep - 1].push_back(des - 1);
	        graph[des - 1].push_back(dep - 1);
	    }
	    
	    bool result = true;
	    for(int i = 0; i < V; i++)
	    {
	        if(visited[i] == 0)
	        {
        	    q.push(i);
        	    visited[0] = 1;
	        }
	    
    	    //DFS 순회 후 출력
    	    while(!q.empty())
    	    {
    	        int current = q.front();
    	        q.pop();
    	        for(int next : graph[current])
    	        {
                    if(visited[next] == 0) 
                    {
                        q.push(next);
                        visited[next] = (-1) * visited[current] + 3;	//반대 그룹으로 만듬
                    }
                    else if(visited[next] == visited[current])
                    {
                        result = false;
                        q = queue<int>();
                        break;
                    }
    	        }
    	    }
	    }
	    if(result) cout << "YES\n";
	    else cout << "NO\n";
	    
	    //동적할당 해제
	    delete[] graph;
	    delete[] visited;    
	}
	return 0;
}

 

그 외


저는 이 문제를 풀 때, 간선의 갯수가 홀수인 순환 형태의 도형이 발견되면 이분 그래프가 아니라 판단했습니다.

 

실제로 답은 맞았지만, 메모리초과가 난 김에 다른 사람의 풀이를 보니 더 정석적인 방법이 있어서 대체했습니다.

 

 

우측 아래처럼 꼭짓점 갯수가 짝수인 것은 포함되지 않습니다.

 

 

 

해당 풀이 :

#include <iostream>
using namespace std;

int V, E;
int **graph;
int *visited;

//스택 없는 DFS 순회
bool dfs(int current, int count)
{
    bool ret = true;
    count++;
    
    //처음 방문하는 노드의 경우, count 저장
    if(visited[current] == 0) visited[current] = count;
    
    //순회를 통해 순환 형태가 나타나면, 에지의 수가 홀수인지 확인하여 false를 반환
    else if((count - visited[current]) % 2 == 1) return false;
    
    
    for(int i = 0; i < V; i++)
    {
        if(graph[current][i] == 1) 
        {
            graph[current][i] = 2;
            graph[i][current] = 2;
            ret = (ret && dfs(i, count));
        }
    }
    return ret;
}


int main() 
{
	int K;
	cin >> K;
	for(int T = 0; T < K; T++)
	{
	    cin >> V >> E;
	    
	    //동적 할당
	    graph = new int*[V];
	    visited = new int[V];
	    fill_n(visited, V, 0);
	    for(int i = 0; i < V; i++)
	    {
	        graph[i] = new int[V];
	        fill_n(graph[i], V, 0);
	    }
	    
	    //입력부
	    for(int i = 0; i < E; i++)
	    {
	        int dep, des;
	        cin >> dep >> des;
	        graph[dep - 1][des - 1] = 1;
	        graph[des - 1][dep - 1] = 1;
	    }
	    
	    //DFS 순회 후 출력
	    if(dfs(0, 0)) cout << "YES\n";
	    else cout << "NO\n";
	    
	    //동적할당 해제
	    for(int i = 0; i < V; i++) delete[] graph[i];
	    delete[] graph;
	    delete[] visited;    
	}
	return 0;
}

 

 

 

그리고 이 문제는 간선 정보를 아래 예시1 처럼 정직하게 이차원 배열로 받으면 메모리 초과가 일어납니다. 필요한 에지의 정보만을 저장해야합니다.

 

예시 1)

011

100

100

 

예시 2)

Edge[0] = {1, 2}

Edge[1] = {0}

Edge[2] = {0]

저작자표시 (새창열림)

'공부 및 정리 > 백준 코드' 카테고리의 다른 글

[C++]백준 - 11279번 문제  (0) 2021.08.17
[C++]백준 - 1753번 문제  (0) 2021.08.17
[C++]백준 - 7562번 문제  (0) 2021.08.13
[C++]백준 - 2206번 문제  (0) 2021.08.13
[C++]백준 - 7579번 문제  (0) 2021.08.13
    '공부 및 정리/백준 코드' 카테고리의 다른 글
    • [C++]백준 - 11279번 문제
    • [C++]백준 - 1753번 문제
    • [C++]백준 - 7562번 문제
    • [C++]백준 - 2206번 문제
    Plite
    Plite
    개발 일지, 게임 이야기 등을 적어두는 공간입니다.

    티스토리툴바