728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/10989

 

10989번: 수 정렬하기 3

첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.

www.acmicpc.net

 

 

문제

N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

 

 


 

접근 방법

수 정렬하기 1번과 2번은 vector의 sort()로 쉽게 풀었는데 이번 문제는 N의 범위가 커서 메모리 초과가 난다.

우선순위 큐를 써도 똑같이 메모리 초과가 난다.

그래서 수의 범위가 10,000을 넘지 않는 점을 고려해서 아래와 같이 코드를 작성했다.

 

* 참고

   ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

를 쓰지 않으면 시간초과가 뜬다. 

 

 

#include <iostream>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    int N; cin >> N;
    int arr[10001] = {0,};
    int num =0;
    for(int i = 0; i<N; i++){
        cin >> num;
        arr[num] += 1;
    }
    for(int i = 1; i<=10000; i++){
        for(int j = 0; j<arr[i]; j++){
            cout << i << "\n";
        }
    }
    
    return 0;
}

 

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/3040

 

3040번: 백설 공주와 일곱 난쟁이

매일 매일 일곱 난쟁이는 광산으로 일을 하러 간다. 난쟁이가 일을 하는 동안 백설공주는 그들을 위해 저녁 식사를 준비한다. 백설공주는 의자 일곱개, 접시 일곱개, 나이프 일곱개를 준비한다.

www.acmicpc.net

 

 

문제

매일 매일 일곱 난쟁이는 광산으로 일을 하러 간다. 난쟁이가 일을 하는 동안 백설공주는 그들을 위해 저녁 식사를 준비한다. 백설공주는 의자 일곱개, 접시 일곱개, 나이프 일곱개를 준비한다.

어느 날 광산에서 아홉 난쟁이가 돌아왔다. (왜 그리고 어떻게 아홉 난쟁이가 돌아왔는지는 아무도 모른다) 아홉 난쟁이는 각각 자신이 백설공주의 일곱 난쟁이라고 우기고 있다.

백설공주는 이런 일이 생길 것을 대비해서, 난쟁이가 쓰고 다니는 모자에 100보다 작은 양의 정수를 적어 놓았다. 사실 백설 공주는 공주가 되기 전에 매우 유명한 수학자였다. 따라서, 일곱 난쟁이의 모자에 쓰여 있는 숫자의 합이 100이 되도록 적어 놓았다.

아홉 난쟁이의 모자에 쓰여 있는 수가 주어졌을 때, 일곱 난쟁이를 찾는 프로그램을 작성하시오. (아홉 개의 수 중 합이 100이 되는 일곱 개의 수를 찾으시오)

입력

총 아홉개 줄에 1보다 크거나 같고 99보다 작거나 같은 자연수가 주어진다. 모든 숫자는 서로 다르다. 또, 항상 답이 유일한 경우만 입력으로 주어진다.

출력

일곱 난쟁이가 쓴 모자에 쓰여 있는 수를 한 줄에 하나씩 출력한다.

 

 


 

 

접근 방법

 

DFS 를 이용해서 9명 중 7명을 뽑았을 때 난쟁이 번호의 합이 100이 되는지 검사한다. 정답을 찾으면 true 를 리턴해서 DFS를 빠져나왔을 때 탐색을 진행하지 않고 종료한다.

 

좀 더 시간을 줄이기 위해 DFS의 첫번째 파라미터를 이용해서 중복 조합으로 난쟁이들을 뽑았다. 두명을 뽑는다고 했을 때, (1번 난쟁이, 2번 난쟁이)를 뽑는 것과 (2번 난쟁이, 1번 난쟁이) 를 뽑는 것이 같기 때문이다.

 

import java.util.*;
public class DFS_BOj3040_백설공주와일곱난쟁이 {

	static int[] numArr, visited;
	static ArrayList<Integer> ansList = new ArrayList<Integer>(); 
	
	public static boolean DFS(int start, int toPick, int sum) {
		if(toPick==0 && sum==100) {
			for(int i = 0; i<ansList.size(); i++) {
				System.out.println(ansList.get(i));
			}
			return true;
		}
		
		for(int i = start; i<9; i++) {
			if(visited[i] == 0) {
				visited[i] = 1;
				ansList.add(numArr[i]);
				if(DFS(i, toPick-1, sum + numArr[i])==false) {
					ansList.remove(ansList.size()-1);
					visited[i] = 0;
				}
				else break;
			}
		}
		return false;
	}
	
	
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		
		numArr = new int[9];
		visited = new int[9];
		
		for(int i = 0; i<9; i++) {
			numArr[i] = sc.nextInt();
		}
		
		DFS(0,7,0);	
	}
}

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/2775

 

2775번: 부녀회장이 될테야

첫 번째 줄에 Test case의 수 T가 주어진다. 그리고 각각의 케이스마다 입력으로 첫 번째 줄에 정수 k, 두 번째 줄에 정수 n이 주어진다

www.acmicpc.net

 

 

문제

평소 반상회에 참석하는 것을 좋아하는 주희는 이번 기회에 부녀회장이 되고 싶어 각 층의 사람들을 불러 모아 반상회를 주최하려고 한다.

이 아파트에 거주를 하려면 조건이 있는데, “a층의 b호에 살려면 자신의 아래(a-1)층의 1호부터 b호까지 사람들의 수의 합만큼 사람들을 데려와 살아야 한다” 는 계약 조항을 꼭 지키고 들어와야 한다.

아파트에 비어있는 집은 없고 모든 거주민들이 이 계약 조건을 지키고 왔다고 가정했을 때, 주어지는 양의 정수 k와 n에 대해 k층에 n호에는 몇 명이 살고 있는지 출력하라. 단, 아파트에는 0층부터 있고 각층에는 1호부터 있으며, 0층의 i호에는 i명이 산다.

입력

첫 번째 줄에 Test case의 수 T가 주어진다. 그리고 각각의 케이스마다 입력으로 첫 번째 줄에 정수 k, 두 번째 줄에 정수 n이 주어진다

출력

각각의 Test case에 대해서 해당 집에 거주민 수를 출력하라.

 

 


 

접근 방법

k, n의 최대값이 14밖에 되지 않으므로 먼저 0층~14층까지 각 층, 각 호에 사는 사람 수를 계산한다.

0층의 i호는 i명이고 각 층의 0호는 0명인 것을 미리 계산한 뒤에 1층부터 채워나가면 된다. 

 

//
//  Comb_BOJ2775_부녀회장이될테야.cpp
//  Coding_Test_Practice
//
//  Created by 김난영 on 2021/05/29.
//  Copyright © 2021 KimNanyoung. All rights reserved.
//

#include <iostream>
using namespace std;

int main(){
    
    int arr[15][15] = {0,};
    for(int i = 0; i<15; i++){
        arr[0][i] = i;  //0층
        arr[i][0] = 0;  //0호
    }
    for(int i = 1; i<15; i++){
        for(int j = 1; j<15; j++){
            arr[i][j] = arr[i][j-1] + arr[i-1][j];
        }
    }
        
    int T; cin >> T;
    int k,n;
    for(int test_case = 0; test_case<T; test_case++){
        cin >> k >> n;
        cout << arr[k][n] << endl;
    }
  
    return 0;
}

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/17071

 

17071번: 숨바꼭질 5

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 500,000)에 있고, 동생은 점 K(0 ≤ K ≤ 500,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때

www.acmicpc.net

 

 

 

문제

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 500,000)에 있고, 동생은 점 K(0 ≤ K ≤ 500,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다. 동생은 항상 걷기만 한다. 동생은 항상 매 초마다 이동을 하며, 이동은 가속이 붙는다. 동생이 이동하는 거리는 이전에 이동한 거리보다 1을 더한 만큼 이동한다. 즉, 동생의 처음 위치는 K, 1초가 지난 후 위치는 K+1, 2초가 지난 후 위치는 K+1+2, 3초가 지난 후의 위치는 K+1+2+3이다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오. 동생을 찾는 위치는 정수 좌표이어야 하고, 수빈이가 0보다 작은 좌표로, 50만보다 큰 좌표로 이동하는 것은 불가능하다.

입력

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

출력

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다. 수빈이가 동생을 찾을 수 없거나, 찾는 위치가 500,000을 넘는 경우에는 -1을 출력한다.

 

 

 


 

 

접근 방법

참고 : (https://imucoding.tistory.com/736)

 

수빈이가 +1, -1로 간다는 점을 주의하자. 즉 다시 제자리로 돌아갈 수 있다! 

 

링크에서의 방법 말고 그냥 보통 BFS 방식으로 풀어서 중복으로 해당 지점에 방문하는 경우에 대해 처리를 해주지 않으면 메모리 초과가 난다. 동생의 위치는 계속해서 증가하므로 visit 배열에 "해당 지점에 도착한 최초 시간"을 저장함으로써 최초 시간 이후에 해당 지점에 도착한 경우에는 continue로 처리해준다. 

 

//
//  BFS_BOJ17071_숨바꼭질5.cpp
//  Coding_Test_Practice
//
//  Created by 김난영 on 2021/05/25.
//  Copyright © 2021 KimNanyoung. All rights reserved.
//

#include <iostream>
#include <queue>
#include <utility>
#include <string.h>

using namespace std;


int visited[2][500001];

bool chk(int loc){
    if(0<=loc && loc <= 500000) return true;
    else return false;
}

int main(){
    
    int N, K;
    cin>> N >> K;

    

    queue<pair<int,int>> que;
    que.push(make_pair(N,0));
    visited[0][N] = 0;      //첫 시작 위치는 0초만에 도달
    memset(visited, -1, sizeof(visited));
    
    while(!que.empty()){
    
        int subinLoc = que.front().first;
        int subinTime = que.front().second;
        que.pop();
          
        if(chk(subinLoc)== false) continue;
        if(visited[subinTime%2][subinLoc]!=-1) continue;
        
        visited[subinTime%2][subinLoc] = subinTime; //해당 위치에 최소 몇초만에 도달했는지 저장     
        
        que.push(make_pair(subinLoc-1, subinTime+1));
        que.push(make_pair(subinLoc+1, subinTime+1));
        que.push(make_pair(subinLoc*2, subinTime+1));
    }
    
    bool flag = false;
    for(int i = 0; i<=500000; i++){
        int nextK = K + (i*(i+1))/2;
        if(nextK > 500000) break;
        if(visited[i%2][nextK] != -1 && visited[i%2][nextK] <= i) {
            cout << i << endl;
            flag = true;
            break;
        }
    }
    if(!flag) cout << -1 << endl;
    
    return 0;
}

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/1774

 

1774번: 우주신과의 교감

(1,1) (3,1) (2,3) (4,3) 이렇게 우주신들과 황선자씨의 좌표가 주어졌고 1번하고 4번이 연결되어 있다. 그렇다면 1번하고 2번을 잇는 통로를 만들고 3번하고 4번을 잇는 통로를 만들면 신들과 선자씨끼

www.acmicpc.net

 

 

문제

황선자씨는 우주신과 교감을 할수 있는 채널러 이다. 하지만 우주신은 하나만 있는 것이 아니기때문에 황선자 씨는 매번 여럿의 우주신과 교감하느라 힘이 든다. 이러던 와중에 새로운 우주신들이 황선자씨를 이용하게 되었다.

하지만 위대한 우주신들은 바로 황선자씨와 연결될 필요가 없다. 이미 황선자씨와 혹은 이미 우주신끼리 교감할 수 있는 우주신들이 있기 때문에 새로운 우주신들은 그 우주신들을 거쳐서 황선자 씨와 교감을 할 수 있다.

우주신들과의 교감은 우주신들과 황선자씨 혹은 우주신들 끼리 이어진 정신적인 통로를 통해 이루어 진다. 하지만 우주신들과 교감하는 것은 힘든 일이기 때문에 황선자씨는 이런 통로들이 긴 것을  좋아하지 않는다. 왜냐하면 통로들이 길 수록 더 힘이 들기 때문이다.

또한 우리들은 3차원 좌표계로 나타낼 수 있는 세상에 살고 있지만 우주신들과 황선자씨는 2차원 좌표계로 나타낼 수 있는 세상에 살고 있다. 통로들의 길이는 2차원 좌표계상의 거리와 같다.

이미 황선자씨와 연결된, 혹은 우주신들과 연결된 통로들이 존재한다. 우리는 황선자 씨를 도와 아직 연결이 되지 않은 우주신들을 연결해 드려야 한다. 새로 만들어야 할 정신적인 통로의 길이들이 합이 최소가 되게 통로를 만들어 “빵상”을 외칠수 있게 도와주자.

입력

첫째 줄에 우주신들의 개수(N<=1,000) 이미 연결된 신들과의 통로의 개수(M<=1,000)가 주어진다.

두 번째 줄부터 N개의 줄에는 황선자를 포함하여 우주신들의 좌표가 (0<= X<=1,000,000), (0<=Y<=1,000,000)가 주어진다. 그 밑으로 M개의 줄에는 이미 연결된 통로가 주어진다. 번호는 위의 입력받은 좌표들의 순서라고 생각하면 된다. 좌표는 정수이다.

출력

첫째 줄에 만들어야 할 최소의 통로 길이를 출력하라. 출력은 소수점 둘째짜리까지 출력하여라.

 

 


 

 

접근 방법

1) 먼저 이미 연결된 M개의 통로에 대해, 이미 연결되었다는 것을 표시하기 위해 노드의 부모 정보를 바꿔준다.

 

2) DFS 함수를 돌려서(중복조합) N개의 우주신 중 2개를 고르고, 우주신 사이의 거리를 구해서 edgeList에 저장한다. 

 

3) edgeList를 거리가 짧은 순으로 정렬한다.

 

4) edgeList에서 통로의 길이와 통로 양쪽에 연결된 우주신 정보를 꺼낸 후 길이가 짧은 순으로 MST를 만들어 나간다.

 

import java.util.*;

class Pair1774{		//해당 노드의 좌표 정보 
	Integer x, y;
	public Pair1774(Integer _x, Integer _y) {
		this.x = _x; 
		this.y = _y;
	}
	public Integer getX() {
		return this.x;
	}
	public Integer getY() {
		return this.y;
	}
}

class Edge1774{		// 통로의 길이와 통로에 연결된 우주신이 몇번째 우주신인지 
	Double dis;
	Integer v1,v2;
	public Edge1774(Double _dis, Integer _v1, Integer _v2) {
		this.dis = _dis;
		this.v1 = _v1;
		this.v2 = _v2;
	}
	public double getDis() {
		return this.dis;
	}
	public Integer getV1() {
		return this.v1;
	}
	public Integer getV2() {
		return this.v2;
	}
}
class Cmp1774 implements Comparator<Edge1774>{
	@Override
	public int compare(Edge1774 e1, Edge1774 e2) {
		if(e1.getDis() < e2. getDis()) return -1;
		else if(e1.getDis() > e2. getDis()) return 1;
		else return 0;
	}
}

public class MST_BOJ1774_우주신과의교감 {
	static Scanner sc = new Scanner(System.in);
	static int N, M;
	static Pair1774[] info = new Pair1774[1001];
	static int[] parent = new int[1001];
	static int[] visited = new int[1001];
	static ArrayList<Edge1774> edgeList = new ArrayList<Edge1774>();
	static double ans;
	static ArrayList<Integer> tmpList = new ArrayList<Integer>();

	//두 점 사이의 거리 리턴. 
	static public double getDistance(int v1, int v2) {
		int x1 = info[v1].getX();
		int y1 = info[v1].getY();
		int x2 = info[v2].getX();
		int y2 = info[v2].getY();

		double res = Math.sqrt(Math.pow((x1-x2), 2) + Math.pow((y1-y2), 2));
		return res;
	}
	
	
	static public int getParent(int x) {
		if(x==parent[x]) return x;
		else return getParent(parent[x]);
	}
	

	//N개 중 2개의 노드 픽 
	public static void pickDFS(int toPick, int start) {
		if(toPick==0) {
			int v1 = tmpList.get(0);
			int v2 = tmpList.get(1);
			double distance = getDistance(v1, v2);
			edgeList.add(new Edge1774(distance, v1, v2));
			return;
		}
		
		for(int i = start; i<=N; i++) {
			if(visited[i]==0) {
				visited[i] = 1;
				tmpList.add(i);
				pickDFS(toPick-1, i);
				tmpList.remove(tmpList.size()-1);
				visited[i] = 0;
			}
		}
		
	}
	
	
	public static void main(String[] args) {
		
		N = sc.nextInt();
		M = sc.nextInt();
		ans = 0;
		
		for(int i = 1; i<=N; i++) {
			int x = sc.nextInt();
			int y = sc.nextInt();
			info[i] = new Pair1774(x,y);
			parent[i] = i;
		}
		
		//이미 연결된 통로 표시 
		for(int i = 0; i<M; i++) {
			int v1 = sc.nextInt();
			int v2 = sc.nextInt();
			if(v1>v2) {
				int tmp = v1;
				v1 = v2;
				v2 = tmp;
			}
			int p1 = getParent(v1);
			int p2 = getParent(v2);
			if(p1!=p2) {
				parent[p2] = p1;
			}
		}
		
		pickDFS(2, 1);	// 2개를 골라서 edgeList에 간선(거리) 정보 저장 
		
		edgeList.sort(new Cmp1774());

		for(int i = 0; i<edgeList.size(); i++) {
			int v1 = edgeList.get(i).getV1();
			int v2 = edgeList.get(i).getV2();
			int p1 = getParent(v1);
			int p2 = getParent( v2);
			if(p1 != p2) {
				parent[p2] = p1;
				ans += getDistance(v1, v2);
			}
		}
		
		System.out.printf("%.2f",ans);
	}
}

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/1922

 

1922번: 네트워크 연결

이 경우에 1-3, 2-3, 3-4, 4-5, 4-6을 연결하면 주어진 output이 나오게 된다.

www.acmicpc.net

 

 

문제

도현이는 컴퓨터와 컴퓨터를 모두 연결하는 네트워크를 구축하려 한다. 하지만 아쉽게도 허브가 있지 않아 컴퓨터와 컴퓨터를 직접 연결하여야 한다. 그런데 모두가 자료를 공유하기 위해서는 모든 컴퓨터가 연결이 되어 있어야 한다. (a와 b가 연결이 되어 있다는 말은 a에서 b로의 경로가 존재한다는 것을 의미한다. a에서 b를 연결하는 선이 있고, b와 c를 연결하는 선이 있으면 a와 c는 연결이 되어 있다.)

그런데 이왕이면 컴퓨터를 연결하는 비용을 최소로 하여야 컴퓨터를 연결하는 비용 외에 다른 곳에 돈을 더 쓸 수 있을 것이다. 이제 각 컴퓨터를 연결하는데 필요한 비용이 주어졌을 때 모든 컴퓨터를 연결하는데 필요한 최소비용을 출력하라. 모든 컴퓨터를 연결할 수 없는 경우는 없다.

입력

첫째 줄에 컴퓨터의 수 N (1 ≤ N ≤ 1000)가 주어진다.

둘째 줄에는 연결할 수 있는 선의 수 M (1 ≤ M ≤ 100,000)가 주어진다.

셋째 줄부터 M+2번째 줄까지 총 M개의 줄에 각 컴퓨터를 연결하는데 드는 비용이 주어진다. 이 비용의 정보는 세 개의 정수로 주어지는데, 만약에 a b c 가 주어져 있다고 하면 a컴퓨터와 b컴퓨터를 연결하는데 비용이 c (1 ≤ c ≤ 10,000) 만큼 든다는 것을 의미한다. a와 b는 같을 수도 있다.

출력

모든 컴퓨터를 연결하는데 필요한 최소비용을 첫째 줄에 출력한다.

 

 

 


 

 

접근 방법

1197번 최소 스패닝 트리 문제와 동일한 방식이다.

받은 간선 정보를 비용을 기준으로 오름차순 정렬한 뒤, 모든 간선에 대해 검사한다.

해당 간선 양쪽에 연결된 노드의 부모를 찾아서 부모가 다른 경우에만 연결해준다. (cost를 더한다)

부모가 같은 경우는 이미 연결된 것을 의미한다.

 

 

주의할 점

간선에 연결된 노드를 검사할 때, 해당 간선에 연결된 노드 v1, v2에 대해 v2의 부모를 v1으로 설정하는 것이 아니라(parent[v2]=v1) 

v2의 부모의 부모를 v2의 부모로 설정하는 것이다. (parent[p1] = p2)

 

import java.util.*;


class Pair1922{
	Integer cost, v1, v2;
	public Pair1922(Integer _cost, Integer _v1, Integer _v2) {
		this.cost = _cost;
		this.v1 = _v1;
		this.v2 = _v2;
	}
	public Integer getCost() {
		return this.cost;
	}
	public Integer getv1() {
		return this.v1;
	}
	public Integer getv2() {
		return this.v2;
	}
}

class Cmp1922 implements Comparator<Pair1922>{
	@Override
	public int compare(Pair1922 p1, Pair1922 p2) {
		if(p1.getCost() < p2.getCost()) return -1;
		else if(p1.getCost() > p2.getCost()) return 1;
		else return 0;
	}
}


public class MST_BOJ1922_네트워크연결 {

	static int[] parent = new int[1001];
	static int getParent(int x) {
		if(x==parent[x]) return x;
		else return parent[x] = getParent(parent[x]);
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		ArrayList<Pair1922> listInfo = new ArrayList<Pair1922>();
		
		int N = sc.nextInt();
		int M = sc.nextInt();
		
		int ans = 0;
		
		for(int i = 0; i<=N; i++) {
			parent[i] = i;
		}

		for(int i = 0; i<M; i++) {
			int a = sc.nextInt();
			int b = sc.nextInt();
			int cost = sc.nextInt();
			
			listInfo.add(new Pair1922(cost, a, b));	
		}
		
		if(listInfo.size()==1 && listInfo.get(0).getv1()==listInfo.get(0).getv2()) {
			ans = listInfo.get(0).getCost();
		}
		
		else {
			listInfo.sort(new Cmp1922());
			
			for(int i = 0; i<listInfo.size(); i++) {
				int v1 = listInfo.get(i).getv1();
				int v2 =  listInfo.get(i).getv2();
				int p1 = getParent(v1);
				int p2 = getParent(v2);
				System.out.println(p1 + " " + p2);
				if(p1==p2) continue;
				else {
					parent[p2] = p1;
					int e = listInfo.get(i).getCost();
					ans += e;
				}	
			}
		}
		
		System.out.println(ans);
		
		
	}
}

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/2805

 

2805번: 나무 자르기

첫째 줄에 나무의 수 N과 상근이가 집으로 가져가려고 하는 나무의 길이 M이 주어진다. (1 ≤ N ≤ 1,000,000, 1 ≤ M ≤ 2,000,000,000) 둘째 줄에는 나무의 높이가 주어진다. 나무의 높이의 합은 항상 M보

www.acmicpc.net

 

 

문제

상근이는 나무 M미터가 필요하다. 근처에 나무를 구입할 곳이 모두 망해버렸기 때문에, 정부에 벌목 허가를 요청했다. 정부는 상근이네 집 근처의 나무 한 줄에 대한 벌목 허가를 내주었고, 상근이는 새로 구입한 목재절단기를 이용해서 나무를 구할것이다.

목재절단기는 다음과 같이 동작한다. 먼저, 상근이는 절단기에 높이 H를 지정해야 한다. 높이를 지정하면 톱날이 땅으로부터 H미터 위로 올라간다. 그 다음, 한 줄에 연속해있는 나무를 모두 절단해버린다. 따라서, 높이가 H보다 큰 나무는 H 위의 부분이 잘릴 것이고, 낮은 나무는 잘리지 않을 것이다. 예를 들어, 한 줄에 연속해있는 나무의 높이가 20, 15, 10, 17이라고 하자. 상근이가 높이를 15로 지정했다면, 나무를 자른 뒤의 높이는 15, 15, 10, 15가 될 것이고, 상근이는 길이가 5인 나무와 2인 나무를 들고 집에 갈 것이다. (총 7미터를 집에 들고 간다) 절단기에 설정할 수 있는 높이는 양의 정수 또는 0이다.

상근이는 환경에 매우 관심이 많기 때문에, 나무를 필요한 만큼만 집으로 가져가려고 한다. 이때, 적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 나무의 수 N과 상근이가 집으로 가져가려고 하는 나무의 길이 M이 주어진다. (1 ≤ N ≤ 1,000,000, 1 ≤ M ≤ 2,000,000,000)

둘째 줄에는 나무의 높이가 주어진다. 나무의 높이의 합은 항상 M보다 크거나 같기 때문에, 상근이는 집에 필요한 나무를 항상 가져갈 수 있다. 높이는 1,000,000,000보다 작거나 같은 양의 정수 또는 0이다.

출력

적어도 M미터의 나무를 집에 가져가기 위해서 절단기에 설정할 수 있는 높이의 최댓값을 출력한다.

 

 

 


 

접근 방법

이진 탐색으로 절단기의 높이 H의 "최대값"을 찾는 문제이다.

 

middle의 값이 M과 같아졌다고 while문을 break 하지 않고, first = middle + 1 의 값으로 정한 뒤, 더 높은 H를 찾아 나가는 것이다. 

 

주의할 점은 처음에 first, last 값을 주어진 나무의 높이 중에 선택해서 초기화하면 안 된다는 것이다. 

그 반례는 아래와 같다. (https://www.acmicpc.net/board/view/23581

 

//input
5 2000000000
900000000 900000000 900000000 900000000 900000000
    
//output
500000000

 

즉 first = 0으로, last = 2,000,000,000(가능한 M의 최대값) 으로 초기화한뒤 while 문을 시작해야한다.

 

import java.util.*;
public class BinarySearch_BOJ2805_나무자르기 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		int N = sc.nextInt();
		int M = sc.nextInt();
		
		ArrayList<Integer> listTree = new ArrayList<Integer>();
		for(int i = 0; i<N; i++) {
			int treeHeight = sc.nextInt();
			listTree.add(treeHeight);
		}
		listTree.sort(null);
		
		long first = 0;
		long last = 2000000000;
		long midHeight=0;
		while(first<=last) {
			
			midHeight = (first+last)/2;
			
			long sumTree = 0;
			for(int i = 0; i<N; i++) {
				long cut = listTree.get(i) - midHeight;
				if(cut<=0) continue;
				sumTree += cut; 
				
			}
			
			if(sumTree>=M) {		//M보다 더 많이 잘랐으면 
				//더 최소한으로 자를 수 있나 즉, 기준 높이 더 높일 수 있 계속 검사 진행 
				first = midHeight+1;
			}
			else last = midHeight -1;	//원하는 M 만큼 못 잘랐으면 
			
		}
		
		System.out.print(last);
	}
}

 

 

728x90
728x90

문제 링크 : https://www.acmicpc.net/problem/16937

 

16937번: 두 스티커

첫째 줄에 모눈종이의 크기 H, W, 둘째 줄에 스티커의 수 N이 주어진다. 다음 N개의 줄에는 스티커의 크기 Ri, Ci가 주어진다.

www.acmicpc.net

 

문제

크기가 H×W인 모눈종이와 스티커 N개가 있다. i번째 스티커의 크기는 Ri×Ci이다. 모눈종이는 크기가 1×1인 칸으로 나누어져 있으며, 간격 1을 두고 선이 그어져 있다.

오늘은 모눈종이에 스티커 2개를 붙이려고 한다. 스티커의 변은 격자의 선과 일치하게 붙여야 하고, 두 스티커가 서로 겹치면 안 된다. 단, 스티커가 접하는 것은 가능하다. 스티커를 90도 회전시키는 것은 가능하다. 스티커가 모눈종이를 벗어나는 것은 불가능하다.

두 스티커가 붙여진 넓이의 최댓값을 구해보자.

입력

첫째 줄에 모눈종이의 크기 H, W, 둘째 줄에 스티커의 수 N이 주어진다. 다음 N개의 줄에는 스티커의 크기 Ri, Ci가 주어진다.

출력

첫째 줄에 두 스티커가 붙여진 넓이의 최댓값을 출력한다. 두 스티커를 붙일 수 없는 경우에는 0을 출력한다.

제한

  • 1 ≤ H, W, N ≤ 100
  • 1 ≤ Ri, Ci ≤ 100

 

 


접근 방법

N개의 스티커 중 DFS 함수를 이용해 2개를 고른다. 이 때 중복 조합을 이용해 중복을 제거한다.

고른 2개의 스티커에 대해 총 4가지 경우를 따져서 스티커를 붙일 수 있는지 검사해야한다.

 

1) 둘 다 그대로

2) 1번 스티커만 회전

3) 2번 스티커만 회전

4) 둘 다 회전

 

각 경우에 대해 위 아래로 붙였을 때와 좌우로 붙였을 때 모두 검사해서 하나라도 모눈종이 안에 들어온다면 스티커 두개 넓이를 계산한다.

 

import java.util.*;

class Pair16937{
	Integer h, w;
	public Pair16937(Integer _h, Integer _w) {
		this.h = _h;
		this.w = _w;
	}
	public Integer getHeight() {
		return this.h;
	}
	public Integer getWidth() {
		return this.w;
	}
}

public class BF_BOJ16937_두스티커 {
	
	static int H, W, N;
	static int[] visited = new int[100];
	static ArrayList<Pair16937> pairList = new ArrayList<Pair16937>();
	static ArrayList<Pair16937> tmpList = new ArrayList<Pair16937>();
	static int maxi = 0;
	
	static boolean chkHeight(int h1, int w1, int h2, int w2) {		//위 아래로 붙였을 때 
		if(h1+h2 > H || Math.max(w1, w2) > W) return false;
		else return true;
	}
	static boolean chkWidth(int h1, int w1, int h2, int w2) {		//양옆으로 붙였을 때 
		if(Math.max(h1, h2) > H || w1 + w2 > W) return false;
		else return true;
	}
	
	static boolean chkHW(int h1, int w1, int h2, int w2) {
		if(chkHeight(h1,w1,h2,w2)==true || chkWidth(h1,w1,h2,w2)==true) return true;
		else return false;
	}
	
	
	static void DFS(int toPick, int start) {
		
		if(toPick==0) {
			int h1 = tmpList.get(0).getHeight();
			int w1 = tmpList.get(0).getWidth();
			
			int h2 = tmpList.get(1).getHeight();
			int w2 = tmpList.get(1).getWidth();
			
			//1번 그대로, 2번 그대로 
			boolean chk = false;
			if(chkHW(h1,w1,h2,w2) == true) {	
				int area = h1*w1 + h2*w2;
				if(maxi<area) maxi = area;
				chk = true;
			}
			else {
				for(int i = 0; i<3; i++) {
					int a,b,c,d;
					if(i==0) {				//1번 회전, 2번 그대로
						a = w1; b = h1;
						c = h2; d = w2;
					}
					else if(i==1) {			//1번 그대로, 2번 회전
						a = h1; b = w1;
						c = w2; d = h2;
					}
					else {					//1번 회전, 2번 회전 
						a = w1; b = h1;
						c = w2; d = h2;
					}
					if(chkHW(a,b,c,d)==true) {
						chk = true;
						int area = h1*w1 + h2*w2;
						if(maxi<area) maxi = area;
						return;
					}
				}
			}
			
			return;
		}
		
		for(int i = start; i<pairList.size(); i++) {
			
			if(visited[i]==0) {
				visited[i] = 1;
				tmpList.add(pairList.get(i));
				DFS(toPick-1, i);
				tmpList.remove(tmpList.size()-1);
				visited[i] = 0;
			}
		}
	}
	
	
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		H = sc.nextInt();
		W = sc.nextInt();
		N = sc.nextInt();
		for(int i = 0; i<N; i++) {
			int h = sc.nextInt();
			int w = sc.nextInt();
			pairList.add(new Pair16937(h,w));
		}
		
		DFS(2,0);
		
		System.out.print(maxi);
	}
}

 

 

 

728x90

+ Recent posts