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

 

1976번: 여행 가자

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인

www.acmicpc.net

 

문제

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자. 물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다. 예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고, 동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.

도시들의 개수와 도시들 간의 연결 여부가 주어져 있고, 동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때 가능한지 여부를 판별하는 프로그램을 작성하시오. 같은 도시를 여러 번 방문하는 것도 가능하다.

입력

첫 줄에 도시의 수 N이 주어진다. N은 200이하이다. 둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다. 다음 N개의 줄에는 N개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.

출력

첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.

 

문제접근 및 풀이

- 처음에 직관적으로 든 생각은 여행계획을 차례차례 따라가면서 bfs를 진행하여 각각의 경로에 대해 가능한 경로가 있는지 확인하는 방법이었다. 당연히 비효율적이고 시간초과가 날것을 알았기에 더 생각해보았다.

- 주어진 인풋배열에 대해 이 칸들을 dp처럼 채워나가면서 모든 정보를 활용해서 온전한 배열(어떻게든 연결되어있다면 1)을 만들 수 있을 것 같다고 생각했다.

- 따라서 플로이드 와샬 알고리즘을 이용해서 해결했다.

- i에서 j로 가는 경로가 0 일때 i에서 k로 가는 것과  k에서 j로 가는 경우가 존재한다면 b[i][j]가 참이 되도록하는 점화식을 가진다.

 

참고

- 여행루트가 1 1 이런식으로 자기자신일때, 해당 값을 참으로 설정해놓지 않으면 반례에 걸리게 되었다. 

 

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#define VMAX 200
#define INF 987654321

using namespace std;

int main(){
    int n, m; cin >> n >> m;
    int b[VMAX][VMAX]={0};
    int s[1000]={0};
    bool pass = true;
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            scanf("%d", &b[i][j]);
            if(i==j) b[i][j]=1; // 반례 => 초기화해주어야 했음
        }
    }
    for(int i=0; i<m; i++)
        scanf("%d", &s[i]);
        
    for(int k=0; k<n; k++){
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                if(b[i][j]==0 && b[i][k]==1 && b[k][j]==1){
                    b[i][j]=1;
                }
            }
        }
    }
    for(int i=0; i<m-1; i++){
        int x = s[i]-1;
        int y = s[i+1]-1;
        if(b[x][y]==0){
            pass=false;
        }
    }
    if(pass){
        printf("YES\n");
    }else{
        printf("NO\n");
    }
    return 0;
}

 

느낀점

- 생각 끝에 예전에 알게되었던 플로이드 와샬을 떠올리고 이걸 적용하면 될것같은데? 하고 해결하게 되어 뿌듯하다.

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

 

1655번: 가운데를 말해요

첫째 줄에는 수빈이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 수빈이가 외치는 정수가 차례대로 주어진다. 정수는 -1

www.acmicpc.net

 

문제

수빈이는 동생에게 "가운데를 말해요" 게임을 가르쳐주고 있다. 수빈이가 정수를 하나씩 외칠때마다 동생은 지금까지 수빈이가 말한 수 중에서 중간값을 말해야 한다. 만약, 그동안 수빈이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다.

예를 들어 수빈이가 동생에게 1, 5, 2, 10, -99, 7, 5를 순서대로 외쳤다고 하면, 동생은 1, 1, 2, 2, 2, 2, 5를 차례대로 말해야 한다. 수빈이가 외치는 수가 주어졌을 때, 동생이 말해야 하는 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 수빈이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 수빈이가 외치는 정수가 차례대로 주어진다. 정수는 -10,000보다 크거나 같고, 10,000보다 작거나 같다.

출력

한 줄에 하나씩 N줄에 걸쳐 수빈이의 동생이 말해야하는 수를 순서대로 출력한다.

 

문제접근 및 풀이

- priority_queue를 2개 사용하여서 해결하였다.

- 하나는 내림차순, 하나는 오름차순 큐를 만들어서 원소를 받는다.

- 원소를 받는 과정에서 두 큐의 크기 차이가 1이 넘지 않도록 한다. -> maxq의 크기가 1개더 크거나 두 큐의 크기가 같도록 한다.

- 이렇게 하면 항상 maxq의 top값이 중간값이 되기 때문이다.

 

priority_queue에 숫자들을 넣고 출력하는 과정

- 첫 원소는 maxq에 집어넣는다.

- 들어오는 원소가 maxq의 top값보다 크면 minq에 집어넣고 아니면 maxq에 집어넣는다.

- 원소를 넣는 과정을 마친 후 maxq의 크기와 minq의 크기를 비교하여 위에서 언급한 조건에 만족하지 않는다면 경우에 따라 top원소를 다른 queue에 push하고 pop하는 과정을 진행해준다.

- 이렇게 되면 maxq의 top값이 항상 중간값이므로 이 값을 출력한다.

 

 

 

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#define VMAX 1001
#define INF 987654321

using namespace std;

int main(){
  int n; cin >> n;
  int a[100000];
  priority_queue<int> minq, maxq;
  for(int i=0; i<n; i++) scanf("%d", &a[i]);

  for(int i=0; i<n; i++){
    int x = a[i];
    if(maxq.empty()){
      maxq.push(x);
    }else{
      if(x > maxq.top()){
        minq.push(-x);
      }else{
        maxq.push(x);
      }
    }
    
    if(maxq.size() -2 == minq.size()){ // maxq 원소가 2개 더 많으면 옮기기
      minq.push(-maxq.top());
      maxq.pop();
    }
    if(maxq.size() +1 == minq.size()){ // minq 원소가 1개 더 많으면 옮기기
      maxq.push(-minq.top());
      minq.pop();
    }
//무조건 max = min+1 이나 max=min 이 되어야 함
    printf("%d\n", maxq.top()); // 무조건 maxq를 출력하도록 q를 조작하기
  }
}

느낀점

처음에 문제를 본 후 heap이나 queue 2개로 구현가능할 것 같다고 생각이 직관적으로 들었다. 하지만 생각을 더 발전시키지 못했고 결국 힌트를 찾은 후 생각을 완성하게 되었다. 조금만 더 생각을 했다면 혼자힘으로 풀 수 있지 않았을까? 하는 아쉬움이 남았다.

문제

N개의 숫자로 구분된 각각의 마을에 한 명의 학생이 살고 있다.

어느 날 이 N명의 학생이 X (1 ≤ X ≤ N)번 마을에 모여서 파티를 벌이기로 했다. 이 마을 사이에는 총 M개의 단방향 도로들이 있고 i번째 길을 지나는데 Ti(1 ≤ Ti ≤ 100)의 시간을 소비한다.

각각의 학생들은 파티에 참석하기 위해 걸어가서 다시 그들의 마을로 돌아와야 한다. 하지만 이 학생들은 워낙 게을러서 최단 시간에 오고 가기를 원한다.

이 도로들은 단방향이기 때문에 아마 그들이 오고 가는 길이 다를지도 모른다. N명의 학생들 중 오고 가는데 가장 많은 시간을 소비하는 학생은 누구일지 구하여라.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 10,000), X가 공백으로 구분되어 입력된다. 두 번째 줄부터 M+1번째 줄까지 i번째 도로의 시작점, 끝점, 그리고 이 도로를 지나는데 필요한 소요시간 Ti가 들어온다. 시작점과 끝점이 같은 도로는 없으며, 시작점과 한 도시 A에서 다른 도시 B로 가는 도로의 개수는 최대 1개이다.

모든 학생들은 집에서 X에 갈수 있고, X에서 집으로 돌아올 수 있는 데이터만 입력으로 주어진다.

출력

첫 번째 줄에 N명의 학생들 중 오고 가는데 가장 오래 걸리는 학생의 소요시간을 출력한다.

 

문제풀이 및 접근

- dijkstra 알고리즘을 이용하였다.

- 각 학생별로 N번 마을에 대해 왔다 갔다하는 경로를 구해야 한다.

1. n번마을에서 모든 학생에게로 가는 최단거리 배열 tdist 값을 구한다.

2. 각 학생별 n 번 마을에 대한 최단경로를 구하면서 구한 값(학생->n 최단거리) + tdist의 배열값(n->학생 최단거리) 중 최대값을 구한다.

 

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#define VMAX 1001
#define INF 987654321

using namespace std;

int n,e,target;
vector<pair<int,int>> a[VMAX];
int dist[VMAX];

int dijkstra(int start){
    priority_queue<pair<int,int>> pq;
    for(int i=0; i<VMAX; i++) dist[i]=INF;
    pq.push({-0, start});
    dist[start]=0;
    while(!pq.empty()){
        int x = pq.top().second;
        int wx = -pq.top().first;
        pq.pop();
        for(int i=0; i<a[x].size(); i++){
            int y = a[x][i].first;
            int wy = a[x][i].second;
            if(dist[y] > dist[x] + wy){
                dist[y] = dist[x] + wy;
                pq.push({-dist[y], y});
            }
        }
    }
    return -1;
}

int main(){
    cin >> n >> e >> target;
    int tdist[VMAX];
    int result=0;
    for(int x,y,w,i=0; i<e; i++){
        scanf("%d %d %d", &x, &y, &w);
        a[x].push_back({y,w});
    }
    
    dijkstra(target);
    for(int i=1; i<=n; i++){
        tdist[i] = dist[i];
    }

    for(int i=1; i<=n; i++){
        if(i==target) continue;
        dijkstra(i);
        result = max(result, tdist[i] + dist[target]);
    }
    cout << result;
}

 

느낀점

처음에는 tdist배열을 따로 두어 비교할 생각을 못해서 dijktras 함수를 따로 하나 더 만들어서 접근했다. 코딩하면서 뭔가 비효율적이라는 생각이 들어서 좀 더 고민을 하다가 tdist배열을 통해 깔끔하게 해결한것 같아 기분이 좋다.

 

경제이론의 역사적인 흐름을 쭉 훑어볼 수 있는 입문서인것같다.

각 경제학자들의 이론적인 내용뿐만 아니라 생애와 그들의 성격, 유년시절에 대한 일화들이 함께 소개되어 있기도 하다.

 

경제학의 창시자 에덤 스미스 <국부론>

- 15세기까지 유럽지성사를 지배한 것은 신학자이다. 17세기부터 종교에서 합리적 이성에 근거한 사고를 추구하게 된다.

- 스미스는 이러한 계몽주의의 시기에 태어났다.(1723~1790)

- 철학자 스미스의 <도덕감정론> :  도덕적 선택의 순간에 처한 사람들은 그것에 대해 깊이 생각하고 충고하는 공평한 관찰자를 마음속에 상정한다.

- 인간이 가지는 동정심과 기본정서에 대해 잘 알고 있었으며 인간의 감정을 탐수하고 분석했다.

 

<국부론> : 부를 창출하는 방법을 설명해줄 인과 법칙

- 인간본성에서 발견한 성향과 자연적 충동

1. 모든 인간은 지금보다 더 잘살고 싶어 한다.

2. 자신의 것을 타인과 교환,교역,거래하고 싶어 한다.

- 국가의 부의 증대를 위해서는 이러한 인간의 자연적 충동을 개발하고 활용해야 한다.

- 이기심은 풍부한 천연자연이며 정부는 이를 억압해서는 안된다.

- 자유시장에서 모든 사람이 자신만의 이익을 추구하면 사회전체가 번영할 것이라고 선언했다.

 

노동분업으로 인한 생산량 향상 근거

1. 맡은 일에 대한 숙련도가 증가한다.

2. 작업 전환이 이루어지는 경우 시간이 단축된다.

3. 같은 작업 반복으로 인한 공구나 기계의 발명

 

- 하지만 노동분업만으로는 국가의 부를 증대시킬 수 없다. 제조업자, 공업자, 마을, 도시간의 자유교역 역시 필수적이다.

- 더 많은 지역간 교역관계가 생겨날수록 국가의 부는 증대된다.

 

<국부론>요약

- 경제성장의 주요 엔진은 노동이다. 노동력 공급 증가, 노동분화, 기계도입으로 인한 노동질 상승와 자유로운 교역이 있을때 경제성장이 가속화된다. 경제성장으로 인해 국민들이 높은 생계 수준을 향유할 수 있다.

 

 

암울한 예언가, 멜서스 <인구론>

- 인구가 25년마다 2배로 증가한다. 식량생산이 결코 이러한 인구증가를 따라오지 못한다.

- 인구가 억제되지 않을 경우 기하급수적으로 증가하지만 식량은 산술급수적으로 증가할 뿐이다.

-> 미래에 대한 비관적인 예측을 함.

 

- 인구 억제 요인 : 1) 적극적 억제 - 전쟁,기아,역병 등 2) 예방적 억제 - 출산률 낮추기

- 기근은 자연이 자신을 보호하기 위한 마지막 수단이다. 인구성장률이 생산력을 뛰어넘기 때문에 인류의 이른 죽음은 필연적인 것이다.

(ps. 멜서스가 인구성장에 대해 비관적인 결론을 내린것은 사실 그의 진리에 대한 사랑과 인류애 때문이다.)

 

- 노동자들이 자신의 생활습관을 바꿀 수 있으며, 도덕적 자제력을 통해 출산률을 낮출 수 있다.

 

평가

- 멜서스의 예측은 빗나갔다. 인구는 기하급수적으로 증가하지 않았으며 식량이 바닥을 치지도 않았다.

- 의학발전, 농업혁명, 녹색혁명(농업생산성 증대),산업혁명의 시작을 간과했다.

- 희소자원을 대신할 대체자원 개발을 고려하지 않았다.

- 종말모델은 기술개발이 자원의 수요를 앞지를 수 없을 것이라는 부정적 가정에 기준해있다.

=> 통계학적 오류 및 시장의 신호나 인간의 노력을 간과하였다.

 

 

자유무역의 화신, 데이비드 리카도 <정치경제학 및 과세의 원리>

- 리카도는 평생토록 정치적 자유와 자유무역을 외쳤다.

 

비교우위론(feat. 스미스의 절대우위론)

- 각자의 기회비용에 따라 잘하는 분야만 전담해 전문화 해야 한다는 것이다.

- 핵심 : 자유무역은 교역 상대국의 경제적 지위에 관계없이 두 나라 모두에게 이롭다. 두 나라 국민 모두가 더 많은 제품을 소비할 수 있기 때문이다.

- 관세정책이 경제성장을 가로막는 경향이 있다.

 

무역이 없고 고립된 경우에 관한 예측

1) 인구증가로 인한 곡물수요가 증가한다.

2) 토지를 늘리기 위해 지농민들이 비옥도가 낮은 토지를 개발한다.

3) 개간 시 발생하는 비용이 증가하고 농사비용이 증가한다.

4) 자연스레 곡물가격이 상승한다.

5) 곡물가격이 오르며 노동자의 임금 또한 상승한다.

6) 사업가들의 이윤이 낮아진다.

7) 비옥한 토지를 가진 지주들의 이윤만 증대된다.

 

 

경제학계의 풍안아, 존 스튜어트 밀 <정치경제원리>

1. 밀의 초기 사상 -> 밴담주의 : 최대다수의 최대행복, 공리주의, 급진주의자

2. 낭만주의 칭송

- 자신의 고삐를 놓아주지 않는 아버지로 인해 합리주의에 절망감, 감정적 매마름을 가졌음.

- 정신적 완성은 고통과 쾌락과 상관없이 그 자체로 한 개인의 목표가 되어야 한다.

3. 테일러와의 결혼 후

- 개량된 밴담주의 : 최대행복은 단순쾌락이 아닌 그 이상의 것이다.

 

<정치경제원리>는 밀이 합리주의와 낭만주의 사이에서 겪었던 지적 고뇌의 산물이다.

- 누진세는 노동의욕을 저하시킨다.(부자에게 소득세 감면)

- 하지만 상송세는 직접노동을 통해 번 재산이 아니기에 공공선을 위해 제한해야 한다.

- 호화스러운 파티 및 사치품에 중과세 부여해야 한다.

- 가난한 사람을 조세대상에서 제외해야 한다.

- 빈민들에게 구제수단을 부여하는 동시에 노동의욕을 고취시키고 신성한 노동윤리를 지키기 위해 공공근로(구제 수당을 받는 대가로 수행하는 노동)가 일반노동자의 노동보다 고역스러워야 한다. 

 

 

비운의 혁명가이자 경제학계의 이단아, 카를 마르크스 <자본론> - 자본주의의 몰락을 예언한 마르크스

- 자본주의의 토대가 무너지고 있으며 대중이 곧 혁명을 일으켜 자본가들을 몰아낼것이다.

- 역사가 인간과 생산의 관계에 따라 노예제 -> 봉건제 -> 자본주의 -> 사회주의로 나아갈 것이다.

- 지배계급은 생산과정의 기술(토지,자본,노동,기술)이 변할때마다 위협에 직면한다.

- 자본주의는 계급제도에 의존하기에 혁명은 불가피하며 노동자 계급의 승리는 자명한것이다.

 

자본주의이 핵심 마르크스의 노동착취론

- 자본가들은 불변자본(공장,설비)를 제공하고 가변자본(노동력)을 고용한다.

- 이때 생산을 통해 발생한 잉여가치는 노동자에게서 강탈한 부정이득,초과가치이다.

- 자본주의에서 실업은 필연적이며 자본가는 생산수단을 통제함으로써 노동시장을 지배한다.

- 자본가는 노동시간을 늘림으로써 이윤을 높인다.

 

자본주의를 파국으로 모는 5가지

1. 이윤률 저하 경향과 자본 축적

- 자본가들 간의 경쟁 심화 -> 노동수요 증가 -> 임금 상승 -> 노동력을 기계로 대체 -> 경쟁을 위해 잉여가치/생산물을 자본화

2. 경제적 집중증가

- 1번의 경쟁의 승자는 결국 물건을 더 싸게 생각하는 대기업이다. 소규모 자본가들은 삭제되거나 대기업에 흡수된다.

3. 깊어지는 경기침체 및 공황

- 노동력을 기계로 대체 -> 실업를 증가 -> 상품판매(즉 수요) 감소 -> 기업도산

4. 산업예비군

- 주기적 불황으로 인해 더 많은 노동자들이 거리로 내몰린다.

5. 프롤레타리아트 계급의 궁핍화

- 모든 부와 권력이 자변가에게 집중/집적되며 노동자의 삶은 피폐해진다.

 

평가

1. 잉여노동 관념의 왜곡

- 마르크스에 의하면, 자본가에게서 가치는 티끌도 나오지 않는다.

- 기업자 정신과 상상력을 간과하였다. 부의 창출을 위해서는  유형재의 투입뿐 아니라 비유형적인 요소가 성공을 위해 필수적이다.

- 역동적이고 관념적인 수많은 요인들을 노의과정에서 간과했다.

 

교훈

1. 경제변화는 상당한 고충을 수반한다.

2. 권력은 언제든 압체로 탈바꿈 가능하다.

3. 피착취계급이 착취로부터 보호받아야 한다.

 

 

앨프리드 마셜의 한계적 사고 <경제학원리>

- 한계주의의 본질 : 점진적으로 증가하는 움직임 분석에 초점을 둔다.

- 얼마나 많은 차량을 생산할지 어떻게 결정하는가? -> 한계비용 > 한계수입 을  만족할때 까지 생상한다.

- 소비자 관점에서는 한계효용과 한계지출의 비교를 통해 행동한다.

- 마셜은 업적이나 명예를 탐하기보다는 소명의식을 갖고 인간의 조건을 향상시키는데 전념했다.

 

탄력성(반응도)

- 탄력성은 상품에 따라 다르다. 1) 대체재의 여부 2) 대체재를 찾을 수 있는 시간(1,2차 오일쇼크) 3) 예산에서 차지하는 비중

- 돔점이 비탄력적인 소비자를 상대할 때 정부는 가격규제를 위해 시장에 개입해야 한다.

- 공익사업의 독점은 규제되어야 한다. -> 자연독점

 

 

자신이 친 제도의 그물에 걸려든 베블린과 갤브레이스

: 구제도학파 베블런 vs 신베도학파 갤브레이스

 

구제도학파 베블런 < 유한계급론>

- 그저 앉아서 부와 재산을 늘리는 사람이 사회에서 존경을 받는다.

- 현시적 소비를 비판. 과고비용은 오로지 소비자 부담이다.

 

엔제니어와 경경자 사이의 갈등

엔지니어 : 필요충족을 원한다. 실용적인 것들을 만들고자 한다.

경영자 : 소비자들을 함정에 빠뜨리고자 한다.

=> 이해관계가 충돌 -> 터무니 없는 경영자들의 생산활동에 격분한 엔지니어 -> 경영자를 몰아낼 것이다.

오늘날 대부분의 ceo는 엔지니어 출신이다.

 

갤브레이스와 광고의 유혹

- 소비자가 기업이 무엇을 생한할지 결정한다는 소비자 주권을 비판하였다.

- 오히려 기업이 자신들의 생산품에 맞춰 소비자들의 수요를 조작한다고 주장한다.

- 광고를 통해 인위적인 욕구를 소비자에게 심을 뿐이다. -> 의존효과!

- 모차르트의 음악은 누군가의 필요에 의해 나타난것이 아니다. 그가 태어났고 음악을 만들고 음악을 듣고 싶어하는 욕구를 불러일으킨 것이다.

 

신제도학파와 경제학

- 신제도학파의 접근법은 법의세계를 파고들었으며, 그들의 논의는 학계에 한정되지 않았다.

- 전통적인 법률분석에 획기적인 변화를 초래한 4가지 주요분야

1. 과실

- PL > C 이면 피고의 과실챔인이 인정된다

P: 사고발생가능성,  L: 사고로 인한 손실과 손해의 정도, C : 사고 예방 비용 

2. 재산

1) 코우즈 정리

- A가 노래부르는데 두는 가치 > B가 잠에 두는 가치 이면 A는 어떻게든 노래를 하려고 할 것이다.

- 즉 법적 판결이 A의 손을 들어줬더라도 그것이 궁극적인 어떤 결과를 가져오지는 못한다는 것이다.

2) 임대료규제법안

- 1970년대 미국정치가 - 주택소유주의 임대료 인상을 규제하여 주택공급을 늘리려고 하였음. 하지만 오히려 주택공급률은 줄어들게 된다.

- 낮은 임대료로 주택수요는 늘었지만 공금이 감소하였기 때문이다. 주택관리 수준 또한 하락했다.

3. 범죄 - 베커의 범죄모델

- 범죄자들이 범죄를 저지르기전에, 검거율과 형량의 경중을 통해 범죄의 비용과 이익을 저울질한다.

- 미래에 대한 별다른 희망이 없는 경우에 범죄를 저지른다.

4. 기업재무

- 전문경영인과 기업소유주간의 불화 -> 대리인을 고용하여 감시

- 전문경영인은 기업의 몸집을 불려 자신의 경력과 몸값을 불리는데만 관심있기 때문이다.

 

베블런과 갤브레이스의 영향

1. 경제학의 정의와 범위를 확장시켰다. 더 넣은 사회현상을 바라보도록 촉구했다.

2. 경제학의 문외한 법률가가 사회를 위험에 빠뜨린다고 경고했다.

3. 경제학은 그것의 대상이 되는 사회 자체만큼 깊고 넓다.

 

 

경제학계의 구세주, 케인스

- 케임브르지 대학 (캠퍼스가 너무 이쁘다)

케인스주의자

- 민간경제는 완전고용에 도달하지 못할 수도 있다.

- 정부지출은 경제를 자극해 완전 고용과 불오나전 고통의 틈을 메울 수 있다.

-> 정부지출을 통한 경기활성화와 소비진작을 위한 세금 인하를 주장했다.

 

세이의 법칙 비판

- 세이의 법칙 : 생품 생산은 노동자, 공급업자가 생산된 모든 상품을 구입할 수 있는 충분한 소득을 가져다 준다.

- 저축과 투자가 자동적으로 연결되지 않는다.

- 유동적이고 가변적인 임금과 물강[ 대해 납득하지 않았다.

-> 케인스는 임금과 물가가 서로 조정되기까지 상당히 오랜시간이 걸린다고 보았다.

케인스의 해법

- 재화와 용역에 대한 총수요가 총소득에 미달할 때 불황이 발생한다.

- 가계와 기업이 충분히 소비하지 않으면 자본가는 생산을 줄이게 되고 이는 노동자의 실업으로 이어진다.

- 해결책은 간단하다. 가계와 기업의 수요를 늘린다 -> 저축가들을 호되게 비판!

- 가계는 총수요에서 가장 중요한 요소이다. 가계지출 규모는 가계 소득에 달려있다. 가계소득증가 -> 가계소비증가

- 즉 완전고용에 도달하기 위해서는 가계소득과 기업투자가 증가해야 한다.

- 정부는 세금삭감이나 지출정책(대규모 공공부문 지출)을 통해 소비부족으로 인한 간극을 메우고 경제를 활성화시켜야 한다. -> 정부의 시장개입

- 세상이 굴러가도록 만드는 것은 인간의 타고난 충동이다. 수학적 기대에 의존하지 않는다.

 

 

케인스에 반기를 든 통화주의 창시자, 밀턴 프리드먼

:현재까지 케인스주의자 vs 통화주의자 간의 대결은 무승부이다. 거시경제학의 이해를 위해서는 이들간에 벌어진 전쟁의 책심을 이해해야 한다.

통화주의자의 케인스주의 비판

1. 정부는 대게 훌륭한 운전사가 되지 못한다.

2. 경제의 가속 페달과 브레이크는 재정정책과 관계가 없다.

-> 가속페탈은 통화량을 늘리는 것이며 브레이크는 통화량을 줄이는 것이다. 의회가 아니라 FRB(연방준비제도, 약칭 연준은 미국의 중앙은행)이 운전석에 앉아야 한다.

 

그렇다면 적정통화량은 어느정도 인가?

- 화폐의 소득유통속도(v)에 따라 달라진다. v가 통화량이 많은 필요가 없다.

- v = GDP / 화폐공급량

 

화페유통속도가 중요한 논쟁의 변수인 이유

- 만약 v가 안정적이라면 중앙은행의 화폐공급량 조절이 강력한 통제 도구가 될 수 있다.

- 만약 v가 불안정하다면 통화량 정책의 효과는 떨어진다.

통화주의자 : 화폐유통속도는 안정적이다! 통화량이 중요하다.

케인스주의자 : 아니다. 불안정하다. 재정정책이 중요하다.

 

- 화폐공급량 조절 도구: 1) 지급준비률 2)FRB이 은행에게 자금 대출 3) 공개시장조작:FRB의 공채

- 공개시장조작을 주로 사용한다.

 

MV = PQ

- M: 화폐공급량, V: 화폐유통속도, P: 물가수준, Q: 생산된 재화와 용역

- PQ는 명목GDP와 같다.

- 변수의 성질을 두고 논쟁이 이루어진다.

1. 통화주의자

- 통화의 유통속도V와 단기적 생산가능한 재화/용역Q는 일정하고 한정되어 있다. 따라서 통화량을 증가시키면 물가수준을 높이는 결과를 만들어낸다. -> 이는 하이퍼인플레이션을 설명하는데 제격이다.

2. 케인스의 비판

- 주로 화폐유통속도를 비판한다.

- 추가로 생긴 돈을 사람들이 쓰지 않으면 통화유통속도는 감소한다. 이러한 현상은 불황시 발생가능성이 증가한다.

- 우리의 통화정책은 소비가 아니라 금리와 투자를 통해 직접적으로 작용한다.

3. 프리드먼의 반격

- 화폐수요는 안정적이다. 건강, 교육, 기대소득 같은 장기적 요인에 의존하기 때문에 장기적으로 변동이 없다.

- 케인스주의에 대한 의문 : 정부의 재정 지출에 필요한 돈은 어디서 나오는가?

- 프리드먼이 옳고 V가 장기적으로 안정적이라고 해도 단기적으로는 불안한 것이 사실이다.

- 통화정책은 단기적으로 물가와 상산량의 영향을 미치지만 장기적으로는 그저 물가에만 영향을 미칠 뿐이다.

- 신세대 경제학자들은 케인스의 재정정책 프리드먼의 통화정책 모두 의미가 있다고 말한다.

 

 

정치는 곧 비즈니스라고 외친 공공선택학파의 창시자, 제임스 뷰캐넌

- 정치인은 정치적 사업가이다. 신뢰할 수 없다. 사업가는 이익극대화를 위해, 정치가는 권력과 능력 극대화를 위해 행동한다.

- 시장이 불완전할 수 있는 것처럼, 정부도불완전 할 수 있다.

 

특수이익집잔의 역설

- 국부증대를 위해 로비하는 것 보다 특수집단의 이익을 위해 로비하는 것이 정치가에게 이득이 된다. 이는 민주주의의 만성적인 문제이다.

- 특정이익집단이 국가 경제 정책에 큰 영향을 미친다. 이익집단의 이익은 늘어나지만, 국가적효율와 소득은 줄어들며 개별소비자의 피해는 그에 따라 증가한다.

- 이 역설은 해결불가능 하다. 역사적인 사례가 없다. 의회가 특수이익집단의 로비에 관심 갖지 않으면 되긴 하지만.

 

예산적자 vs 예산흑자

- 적자의 타격은 분산적이고 간접적이다. -> 하지만 겉으로 보이지 않는 과제아닌가?

- 흑자의 경우 세금증가 혹은 정부지출삭감을 동반해야 함으로 직접적인 타격이 온다.

- 막대한 예산을 통해 사업을 진행하는 것(ex. 200억 달러 도로건설 예산)은 도로를 금으로 도배하는 것과 마찬가지 이다. 하지만 이는 정치인들이 자신의 경력마저 금빛으로 화려하게 장식하는 것과 다름없다. -> 해당 정책 지지 정치인 98% 재선성공

 

정치주기

- 재선 확률을 높이기 위해 거시 경제 지표를 조작하는 것. ex. 선거1년 전부터 경기활성화를 위해 통화정책 펴도록 촉구

- 중앙은행 관리자와 정치가 사이의 모조으이 공모관계가 깊어진다.

 

케인스가 공공선택학파의 출현을 예견하지 못한 이유 - 왜 정부의 구조적 결합을 경고하지 않았을까?

1. "공공선은 최고의 법칙이다"라는 결과론에 얽메여있었다.

2. 정부가 엘리트들에 의해 통지될 수 있다고 믿었다.

=> 관료들이 공공선이 아닌 이기심에 기초해 행동할 수 있다는 것을 예측하지 못했다.

=> 자신의 자시/설득력에 대한 강한자신감, 성장배경(관료들의 문제점을 이기주의가 아닌 행동하지 않는 것이라 생각함)

 

 

합리적기대와 불확실성이 동시에 지배하는 기상천외의 세계 <합리적 기대이론 학파>

- 정부의 개입이 경제에 영향이 미친다는 것은 환상이다!!

- 모든 시장은 완전하다. 생산이 증가하면 가격을 떨어지고 노동력수요가 떨어지면 임금도 떨어진다. 바로바로 조절가능하다.

- 사람들이 자신들의 경제적 결정에 있어서 가능한 모든 정보를 분석, 수집하고 기대를 갱신한다.

=> 현실적으로 시간의 영향과 사람이 불합리성을 간과했다. 현실성 zero

 

- 합리적기대이론은 주식시장에서는 잘 통한다.

- 주식시작은 효율적이고 유동성이 높기 때문이다.

- 하지만 실물시장은 수직시장보다 더 복잡하고 경직되어 있다.

 

먹구름, 그리고 한줄기 햇살

한가지 분명한 사실

- 정부와 시장은 상호작용한다.

- 정부의 본질은 완전한 악도 완전한 선도 아니다.

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는 같을 수도 있다.

출력

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

 

문제접근 및 풀이

- 문제를 해결한 후에 찾아보니 최소스패닝트리라는 개념의 문제라고 한다.

1.각 노드마다 최소가중치를 찾기위해 priority_queue를 이용한다.

2. 최소가중치를 갖는 노드를 하나 찾는다. -> vector에 추가

3. 이 노드를 시작으로 그래프를 넓혀나가기 시작한다.

  3.1 vector에 추가된 각 노드에 해당하는 큐의 top값 중 최소값을 갖는 간선을 선택한다.

  3.2 만일 이 간선이 이미 저장된 노드들을 연결하는 것이라면 필요없으므로 pop한다.

  3.3 새로운 노드와 연결되는 간선이라면 새로운 노드를 벡터에 추가하고 result에 해당 가중치를 더한다.

  3.4 벡터의 크기가 주어진 노드의 수가 될때까지 반복한다.

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#define VMAX 1001
#define INF 987654321

using namespace std;

vector<int> findN;
priority_queue<pair<int,int>> pq[VMAX];
int n,e;
int result=0;

int solve(int x){
  findN.push_back(x);
  while(1){
    if(findN.size() == n) break;
    int minW=INF, minX, minY;
    for(int i=0; i<findN.size(); i++){
      int cur = findN[i];
      if(!pq[cur].empty()){ 
        // 지금까지 만들어진 그래프의 중 한 노드에 대해 - 노드 값이 존재하면
        int w = -pq[cur].top().first;
        int y = pq[cur].top().second;
        if(minW > w){
          minW = w;
          minX = cur;
          minY = y;
        }
      }
    }
    if(find(findN.begin(), findN.end(), minY) == findN.end()){ 
      // 없음 -> 즉 통과, 추가해야함
      findN.push_back(minY);
      result+=minW;
    }else{
      pq[minX].pop();
    }
  }
  return result;
}

int main(){
  cin >> n >> e;
  int minW=INF, minX, x;
  for(int y,w,i=0; i<e; i++){
    scanf("%d %d %d", &x, &y, &w);
    pq[x].push({-w, y});
    pq[y].push({-w, x});
    if(minW > w){
      minW = w;
      minX = x;
    }
  }
  cout << solve(minX);
  return 0;
}

 

느낀점 

1. (거의)혼자의 힘으로 최소스패닝트리라는 개념을 떠올려내고 고비가 있었지만 구현을 성공시켜서 뿌듯하다!

2. 고비,,, -> solve함수로 넘기는 변수를 x라고 해놓고 함수안에서 최소 가중치를 갖는 노드의 출발지를 갱신할때 cur가 아니라x라고 해놓은것 때문에 버그를 거의 1시간 내내 찾았다. 다음부터는 변수명을 좀더 명확하게 해야겠다 ㅠㅠ

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

 

1600번: 말이 되고픈 원숭이

첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있

www.acmicpc.net

문제

동물원에서 막 탈출한 원숭이 한 마리가 세상구경을 하고 있다. 그 녀석은 말(Horse)이 되기를 간절히 원했다. 그래서 그는 말의 움직임을 유심히 살펴보고 그대로 따라 하기로 하였다. 말은 말이다. 말은 격자판에서 체스의 나이트와 같은 이동방식을 가진다. 다음 그림에 말의 이동방법이 나타나있다. x표시한 곳으로 말이 갈 수 있다는 뜻이다. 참고로 말은 장애물을 뛰어넘을 수 있다.

  x   x  
x       x
       
x       x
  x   x  

근데 원숭이는 한 가지 착각하고 있는 것이 있다. 말은 저렇게 움직일 수 있지만 원숭이는 능력이 부족해서 총 K번만 위와 같이 움직일 수 있고, 그 외에는 그냥 인접한 칸으로만 움직일 수 있다. 대각선 방향은 인접한 칸에 포함되지 않는다.

이제 원숭이는 머나먼 여행길을 떠난다. 격자판의 맨 왼쪽 위에서 시작해서 맨 오른쪽 아래까지 가야한다. 인접한 네 방향으로 한 번 움직이는 것, 말의 움직임으로 한 번 움직이는 것, 모두 한 번의 동작으로 친다. 격자판이 주어졌을 때, 원숭이가 최소한의 동작으로 시작지점에서 도착지점까지 갈 수 있는 방법을 알아내는 프로그램을 작성하시오.

입력

첫째 줄에 정수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있는 곳으로는 이동할 수 없다. 시작점과 도착점은 항상 평지이다. W와 H는 1이상 200이하의 자연수이고, K는 0이상 30이하의 정수이다.

출력

첫째 줄에 원숭이의 동작수의 최솟값을 출력한다. 시작점에서 도착점까지 갈 수 없는 경우엔 -1을 출력한다.

 

 

문제접근 및 풀이

- 일반적인 bfs로 접근하여 쉽게 풀수 있는 문제이다. 

- 다만 말이동이라는 추가적인 조건이 붙어있기에 이를 고려해주어야 한다.

- 같은 좌표에 도달하더라도 k 값 즉 말 이동 횟수에 따라 다른 상태를 나타내므로 말 이동 횟수에 따라 방문체크를 달리해줘야 한다. 말이동을 해서 특정칸에 온 경우와 말이동을 쓰지 않고 온경우는 다르게 봐야하기 때문이다.

- 따라서 3차원 방문배열을 사용해야 한다. (x, y, k)

 

#include <iostream>
#include <algorithm>
#include <queue>
#define VMAX 200
#define KMAX 31

using namespace std;

int k,w,h;
int b[VMAX][VMAX];
bool v[VMAX][VMAX][KMAX]={0};
int dx[12]={0, 0, 1, -1, -1, -2, -2, -1, 1, 2, 2, 1};
int dy[12]={1, -1, 0, 0, -2, -1, 1, 2, 2, 1, -1, -2}; // 0~3은 인접이동, 4~11은 말이동
void bfs(int sk){
  queue<pair<pair<int,int>,pair<int,int>>> q; // x,y, depth, k
  q.push({{0,0},{0,0}}); 
  v[0][0][0]=true;
  while(!q.empty()){
    int x = q.front().first.first;
    int y = q.front().first.second;
    int depth = q.front().second.first;
    int k = q.front().second.second;
    q.pop();
    if(x==h-1 && y==w-1){
      printf("%d", depth);
      return ;
    }
    for(int i=0; i<4; i++){
      int X = x + dx[i];
      int Y = y + dy[i];
      if(X>=0 && Y>=0 && X<h && Y<w && v[X][Y][k]==false && b[X][Y]==0){
        v[X][Y][k]=true;
        q.push({{X,Y},{depth+1,k}});
      }
    }
    if(sk > k){
      for(int i=4; i<12; i++){
      int X = x + dx[i];
      int Y = y + dy[i];
      if(X>=0 && Y>=0 && X<h && Y<w && v[X][Y][k+1]==0 && b[X][Y]==0){
        v[X][Y][k+1]=1;
        q.push({{X,Y},{depth+1,k+1}});
      }
    }
    }
  }
  printf("-1");
}

int main(){
  cin >> k;
  cin >> w >> h;
  for(int i=0; i<h; i++){
    for(int j=0; j<w; j++){
      scanf("%d", &b[i][j]);
    }
  }
  bfs(k);
  return 0;
}

 

느낀점

문제풀이 중간에 정답이 아닐 이유가 도저히없는데,, 왜 안되는거지?? 라는 생각을 가졌다. 한 50여분 고민끝에 답이 나오지 않자 나와 비슷하게 접근한 다른 사람의 코드는 한줄한줄 비교해 나갔다. 거의 다 비교가 끝나갈 무렵에도 내가 맞았다는 확신에 차있었다. 하지만 결국 내가 실수했다는 것을 깨닫게 되었다.

말이동을 할 경우 방문체크를 할때 k를 1 늘린 방문배열에 대해 확인을 해줘야 하는데 현재 k 값에 대해 방문체크를 하고 있었다...

-> 앞으로 생각한 후 코드를 짰는데도 오류가 있다면 반례 혹은 오류를 발견할때 까지 1시간정도의 시간을 소요해서 생각을 하는 시간을 갖자.

'알고리즘 문제풀이' 카테고리의 다른 글

[백준] 1238번 파티 c++  (0) 2021.08.12
[백준] 1922번 네트워크 연결 c++  (0) 2021.08.11
[백준] 11048번 이동하기 c++  (0) 2021.08.06
[백준] 2573번 빙산 c++  (0) 2021.08.06
[백준] 5014번 스타트링크 c++  (0) 2021.08.05

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

 

11048번: 이동하기

준규는 N×M 크기의 미로에 갇혀있다. 미로는 1×1크기의 방으로 나누어져 있고, 각 방에는 사탕이 놓여져 있다. 미로의 가장 왼쪽 윗 방은 (1, 1)이고, 가장 오른쪽 아랫 방은 (N, M)이다. 준규는

www.acmicpc.net

 

문제

준규는 N×M 크기의 미로에 갇혀있다. 미로는 1×1크기의 방으로 나누어져 있고, 각 방에는 사탕이 놓여져 있다. 미로의 가장 왼쪽 윗 방은 (1, 1)이고, 가장 오른쪽 아랫 방은 (N, M)이다.

준규는 현재 (1, 1)에 있고, (N, M)으로 이동하려고 한다. 준규가 (r, c)에 있으면, (r+1, c), (r, c+1), (r+1, c+1)로 이동할 수 있고, 각 방을 방문할 때마다 방에 놓여져있는 사탕을 모두 가져갈 수 있다. 또, 미로 밖으로 나갈 수는 없다.

준규가 (N, M)으로 이동할 때, 가져올 수 있는 사탕 개수의 최댓값을 구하시오.

입력

첫째 줄에 미로의 크기 N, M이 주어진다. (1 ≤ N, M ≤ 1,000)

둘째 줄부터 N개 줄에는 총 M개의 숫자가 주어지며, r번째 줄의 c번째 수는 (r, c)에 놓여져 있는 사탕의 개수이다. 사탕의 개수는 0보다 크거나 같고, 100보다 작거나 같다.

출력

첫째 줄에 준규가 (N, M)으로 이동할 때, 가져올 수 있는 사탕 개수를 출력한다.

 

접근방법 및 풀이

- 점화식을 구해 dp로 풀 수 있다.

- i,j번째 값은 i-1,j / i,j-1 / i-1,j-1번째 dp배열값중 최대값 +  i,j번째 칸의 사탕개수 이다.

- 첫번째 열과 첫번째 행에 대해 구간합을 구해서 초기 dp배열을 세팅해준다.

#include <iostream>
#include <algorithm>
#define VMAX 1001

using namespace std;

int main(){
  int n,m; cin >> n >> m;
  int s[VMAX][VMAX]={0};
  int d[VMAX][VMAX]={0};
  for(int i=1; i<=n; i++){
    for(int j=1; j<=m; j++){
      scanf("%d", &s[i][j]);
      if(i==1 || j==1) d[i][j] = s[i][j];
    }
  }
  for(int i=1; i<=n; i++){
    d[i][1]+=d[i-1][1];
  }
  for(int j=1; j<=m; j++){
    d[1][j]+=d[1][j-1];
  }
  for(int i=2; i<=n; i++){
    for(int j=2; j<=m; j++){
      d[i][j] = max({d[i][j-1], d[i-1][j], d[i-1][j-1]}) + s[i][j];
    }
  }
  printf("%d", d[n][m]);
}

느낀점

처음에는, 소위 dp문제라고 하는 것들을 백트레킹으로 재귀적으로 탐색하는 방법으로 풀었는데 점화식을 사용하여 좀 더 직관적으로 풀려서 재밌었다.

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

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을

www.acmicpc.net

 

문제

지구 온난화로 인하여 북극의 빙산이 녹고 있다. 빙산을 그림 1과 같이 2차원 배열에 표시한다고 하자. 빙산의 각 부분별 높이 정보는 배열의 각 칸에 양의 정수로 저장된다. 빙산 이외의 바다에 해당되는 칸에는 0이 저장된다. 그림 1에서 빈칸은 모두 0으로 채워져 있다고 생각한다.

             
  2 4 5 3    
  3   2 5 2  
  7 6 2 4    
             

그림 1. 행의 개수가 5이고 열의 개수가 7인 2차원 배열에 저장된 빙산의 높이 정보

빙산의 높이는 바닷물에 많이 접해있는 부분에서 더 빨리 줄어들기 때문에, 배열에서 빙산의 각 부분에 해당되는 칸에 있는 높이는 일년마다 그 칸에 동서남북 네 방향으로 붙어있는 0이 저장된 칸의 개수만큼 줄어든다. 단, 각 칸에 저장된 높이는 0보다 더 줄어들지 않는다. 바닷물은 호수처럼 빙산에 둘러싸여 있을 수도 있다. 따라서 그림 1의 빙산은 일년후에 그림 2와 같이 변형된다.

그림 3은 그림 1의 빙산이 2년 후에 변한 모습을 보여준다. 2차원 배열에서 동서남북 방향으로 붙어있는 칸들은 서로 연결되어 있다고 말한다. 따라서 그림 2의 빙산은 한 덩어리이지만, 그림 3의 빙산은 세 덩어리로 분리되어 있다.

             
    2 4 1    
  1   1 5    
  5 4 1 2    
             

그림 2

             
      3      
        4    
  3 2        
             

그림 3

한 덩어리의 빙산이 주어질 때, 이 빙산이 두 덩어리 이상으로 분리되는 최초의 시간(년)을 구하는 프로그램을 작성하시오. 그림 1의 빙산에 대해서는 2가 답이다. 만일 전부 다 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 프로그램은 0을 출력한다.

입력

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지막 행과 열에는 항상 0으로 채워진다.

출력

첫 줄에 빙산이 분리되는 최초의 시간(년)을 출력한다. 만일 빙산이 다 녹을 때까지 분리되지 않으면 0을 출력한다.

 

문제접근 및 풀이

- 1년이 지날때 마다 bfs를 통해 이분화되었는지를 확인한다.

- 각 년도 마다 모든 바닷물 칸에 대해 인접한 빙산들의 높이를 한칸씩 줄인다.

- 모든 칸에 대해 탐색하면서 빙산이 있고 아직 탐색하지 않은 자리가 발견되면 bfs를 진행한다.

- bfs가 2번이상 진행된 경우 분열된것이므로 해당 년수 출력

- bfs가 0번 진행된 경우 이분화되지 않은 채 모든 빙산이 녹아 없어진 것이므로 0출력

#include <iostream>
#include <algorithm>
#include <queue>
#define VMAX 301

using namespace std;

int n,m;
int b[VMAX][VMAX];
bool c[VMAX][VMAX];
int v[VMAX][VMAX]={0};  

int result=0;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int countb=0;
int years=0;

void bfs(int sx, int sy, int v[][VMAX]){
  queue<pair<int,int>> q;
  q.push({sx,sy});
  while(!q.empty()){
    int x = q.front().first;
    int y = q.front().second;
    q.pop();
    //printf("x:%d y:%d\n",x,y);
    for(int i=0; i<4; i++){
      int X = x+dx[i];
      int Y = y+dy[i];
      if(b[X][Y]!=0 && v[X][Y]==0){ // 빙산이 있고 방문안했으면
        v[X][Y]=1;
        q.push({X,Y});
      }
    }
  }
}

int main(){
  cin >> n >> m;
  for(int i=0; i<n; i++){
    for(int j=0; j<m; j++){
      scanf("%d", &b[i][j]);
    }
  }
  while(1){
    for(int i=0; i<=n; i++){ // 빙산체크초기화
      for(int j=0; j<m; j++){
        if(b[i][j]==0) c[i][j]=false; // 빙산이 있으면 true
        else c[i][j] = true;
      }
    }

    countb=0;

    for(int i=0; i<n; i++)
      for(int j=0; j<m; j++)
        v[i][j]=0;
    
    for(int i=1; i<n-1; i++){ // BFS로 이분되었는지 탐색
      for(int j=1; j<m-1; j++){
        if(b[i][j]!=0 && v[i][j]==0){
          bfs(i,j, v);
          countb++;
        }
      }
    }
    if(countb>=2) break;
    if(countb==0){ // 모든 빙산이 없어졌으므로 끝까지 분리되지 못한것이다.
      printf("0");
      return 0;
    }
    years++;
    for(int i=1; i<n-1; i++){ // 1년 후 시간진행
      for(int j=1; j<m-1; j++){
        for(int k=0; k<4; k++){
          int X = i+dx[k];
          int Y = j+dy[k];
          if(c[X][Y]==false && b[i][j] > 0){
            b[i][j]--;
          }
        }
      }
    }

  }
  printf("%d", years);
}

느낀점

생각정리가 잘 되었고 한번에 풀려서 뿌듯했다.

+ Recent posts