2016년 11월 24일 목요일

MongoDB 설치 후 저장 디렉토리 변경 주의사항

MongoDB를 설치하기 위해 공식 도큐먼트(https://docs.mongodb.com/.../install-mongodb-on-ubuntu/)를 참고하였다.

정상적으로 설치된 것을 확인하고 이후에 기본 저장 디렉토리를 변경하기 위해서 /etc/mongod.conf 파일을 건드렸다.

# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.
storage:
#  dbPath: /var/lib/mongodb
  dbPath: /data/mongodb
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1


#processManagement:

#security:

#operationProfiling:

#replication:

#sharding:

## Enterprise-Only Options:

#auditLog:

#snmp:

dbpath를 /data/mongodb 로 변경하고 /data/mongodb 디렉토리를 만들어줬다.

$ sudo mkdir /data && sudo mkdir /data/mongodb

그리고 다시 mongodb를 구동하면 실행에 실패한다.

$ sudo service mongod status
● mongod.service - High-performance, schema-free document-oriented database
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Thu 2016-11-24 04:49:20 UTC; 3s ago
     Docs: https://docs.mongodb.org/manual
  Process: 5335 ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf (code=exited, status=100)
 Main PID: 5335 (code=exited, status=100)

mongodb에서 해당 디렉토리에 접근 권한이 없기 때문이다.

다음과 같이 권한을 추가해주면 정상적으로 구동된다.

$ chown mongodb:mongodb /data/mongodb

정상적으로 작동하는 모습

$ sudo service mongod status
● mongod.service - High-performance, schema-free document-oriented database
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: active (running) since Thu 2016-11-24 04:50:04 UTC; 5s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 5420 (mongod)
    Tasks: 17
   Memory: 32.1M
      CPU: 130ms
   CGroup: /system.slice/mongod.service
           └─5420 /usr/bin/mongod --quiet --config /etc/mongod.conf

2016년 11월 1일 화요일

2022 - 사다리

https://www.acmicpc.net/problem/2022
https://uva.onlinejudge.org/...&problem=1507


\(a, b, c\) 가 주어지면 \(k\) 를 구해내는 문제이다.

피타고라스로 A, B를 구해서 기울기를 구하고, 두 직선의 교점 방정식을 이용했다. 그리고 교점의 y 위치가 \(c\) 가 되는 순간을 구하도록 이분 탐색을 했다.

우선 a가 포함된 직선을 g(x), b가 포함된 직선을 f(x)라 한다면 아래와 같은 정보가 나온다.

\(
\begin{cases}
A = \sqrt{a^2 - k^2} \\
B = \sqrt{b^2 - k^2}
\end{cases}
\)

\(
\begin{cases}
f(x) = \frac{B}{k}x \\
g(x) = -\frac{A}{k}x+A
\end{cases}
\)

두 직선이 만나는 순간을 \( (c_0, c) \)라 한다면 \( f(c_0) = g(c_0) = c \) 이여야 한다.

\(
\begin{align}
f(c_0) = & \frac{B}{k}c_0 = c \\
& c_0 = \frac{k \cdot c}{B}
\end{align}
\)

\(g(c_0) = g(\frac{k \cdot c}{B}) = c\) 가 나온다면 정답일것이다.

k를 찾는 과정에서 \(f(c_0) \gt c\) 라면 높이가 더 높은 좌표이므로 \(c_0\)을 줄여야한다는 뜻이다. k를 줄이면 \(c_0\)도 줄어든다.

k를 조정해서 만들어진 적당한 x로 f(x) = g(x)가 성립하는 지 확인하면서, 결과에 따라 k를 다시 조정하면 정답이 나온다.

k를 찾는 구간을 줄일 때 epsilon을 1e-5로 설정했을 때는 오답이었다. 이분탐색이 중간에 잘못되고 있나 생각에 구글링하다가 더 작은 결과까지 해보도록 1e-9 로 바꿨더니 정답을 맞았다. 너무 허무함

#include <bits/stdc++.h>
using namespace std;

#define INF 987654321

double a, b, c;

double g(double x, double k){
    double A = sqrt(a*a - k*k);
    return A - (A * x / k);
}

double f(double x, double k){
    return sqrt(b*b - k*k) * x / k;
}

int main(){
    while(~scanf("%lf %lf %lf", &a, &b, &c)){
        double l=0, r=min(a, b);
        
        while(r - l > 1e-9){
            double k = (l+r)/2.0;
            double c0 = k * c / sqrt(b*b - k*k);
            if(g(c0, k) > c){
                l = k;
            } else {
                r = k;
            }
        }
        
        printf("%.3lf\n", l);
    }
    return 0;
}

1939 - 중량제한

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

S에서 E로 가는 데, 한 번의 이동에서 옮길 수 있는 물품들의 중량의 최대값을 구한다.

문제를 읽고 왠지 이거 다익스트라처럼 그리디하게 풀면 풀리겠다 싶어서 그래프 몇개 끄적거렸는데 무한루프에 빠질것만 같은 공포에 휩싸였다.

dp를 적용하자니 다리의 중량제한 \(c(1 \le c \le 10^9)\) 라서 안되겠다싶었고.

그럼 스패닝 트리를 만들어볼까해서 프림을 생각했는데 몇 개 적어놨던 예제에서부터 안됐다.

그래도 트리로 만드는게 가장 정답같아서 크루스칼로 구현했는데 정답이었다.

크루스칼로 최대 스패닝 트리를 만들고, S에서 E로 가는 경로 중에 가장 중량제한이 작은 것을 출력한다.

#include <bits/stdc++.h>
using namespace std;

#define INF 987654321

typedef pair<int,int> ii;

struct edge {
    int u, v, d;
    bool operator < (const edge& e) const {
        return d < e.d;
    }
};

vector<edge> E;

int group[100001];

int find(int v){
    if(v != group[v])
        return (group[v] = find(group[v]));
    return v;
}

bool Union(int p, int q){
    p = find(p);
    q = find(q);
    if(p == q) return false;
    group[q] = p;
    return true;
}

vector<ii> adj[100001];

bool visit[100001];
int answer(int pos, int dst, int m = INF){
    if(pos == dst) return m;
    
    if(visit[pos]) return 0;
    visit[pos] = true;
    
    for(auto& v : adj[pos]){
        int r = answer(v.first, dst, min(v.second, m));
        if(r != 0) return r;
    }
    return 0;
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    while(m--){
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        E.push_back({a-1, b-1, c});
    }
    sort(E.rbegin(), E.rend());
    
    for(int i=0; i<n; ++i) group[i] = i;
    
    for(auto& e : E){
        if( Union(e.u, e.v) ){
            adj[e.u].push_back(ii(e.v, e.d));
            adj[e.v].push_back(ii(e.u, e.d));
        }
    }
    
    int src, dst;
    scanf("%d %d", &src, &dst);
    printf("%d", answer(src-1, dst-1));
    
    return 0;
}

다른 사람들 정답 코드를 보니까 다익스트라도 있었는데, 크루스칼로 푼 사람들 중에 하나를 보고 배웠다.

S에서 E로 가는 경로를 다시 탐색할 필요가 없이, S가 속한 집합과 E가 속한 집합이 같아지는 시점에 연결하는 간선의 중량이 "가능한 가장 큰 중량제한"이었다.

#include <bits/stdc++.h>
using namespace std;
 
#define INF 987654321
 
typedef pair<int,int> ii;
 
struct edge {
    int u, v, d;
    bool operator < (const edge& e) const {
        return d < e.d;
    }
};
 
vector<edge> E;
 
int group[100001];
 
int find(int v){
    if(v != group[v])
        return (group[v] = find(group[v]));
    return v;
}

int main(){
    int n, m;
    scanf("%d %d", &n, &m);
    while(m--){
        int a, b, c;
        scanf("%d %d %d", &a, &b, &c);
        E.push_back({a-1, b-1, c});
    }
    sort(E.rbegin(), E.rend());
     
    for(int i=0; i<n; ++i) group[i] = i;
     
    int src, dst;
    scanf("%d %d", &src, &dst);
     
    for(auto& e : E){
        group[find(e.u)] = find(e.v);
        if( find(src-1) == find(dst-1) ){
            printf("%d", e.d);
            break;
        }
    }
     
    return 0;
}

2016년 10월 31일 월요일

2146 - 다리 만들기

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

각 대륙마다 번호를 붙여서 동기적으로 대륙을 넓혀간다. 넓어가는 도중에 다른 대륙과 만난다면, 그 시점에 각 대륙이 넓혀진 거리만큼이 (부딪혔으므로) 연결된 거리이고, 그 중에 최소를 구한다.

동기적으로 넓혀가는 이유는 대륙 하나를 잡고 나머지와 비교하면 아래와 같은 경우가 생긴다.
대륙간의 거리를 모두 비교하면 \(O(N^2)\)이다. 물론 N이 최대 100까지이므로 모두 비교해도 문제될 건 없다.

#include <bits/stdc++.h>
using namespace std;

#define INF 987654321

int n, a[100][100];

int dy[]={-1,1,0,0};
int dx[]={0,0,-1,1};

bool visit[100][100];

bool giveId(int y, int x, const int id){
    if(y < 0 || y >= n || x < 0 || x >= n) return 0;
    
    if(visit[y][x] || a[y][x] == 0) return 0;
    visit[y][x] = true;
    
    a[y][x] = id;
    for(int i=0; i<4; ++i){
        giveId(y+dy[i], x+dx[i], id);
    }
    return 1;
}

struct land {
    int y, x, color;
};

int Distance[100][100];

int main(){
    scanf("%d ", &n);
    for(int i=0; i<n; ++i){
        for(int j=0; j<n; ++j){
            scanf("%d", &a[i][j]);
            Distance[i][j] = a[i][j] ? 0 : INF;
        }
    }
    
    int color = 1;
    for(int i=0; i<n; ++i){
        for(int j=0; j<n; ++j){
            color += giveId(i, j, color);
        }
    }
    
    queue<land> q;
    for(int i=0; i<n; ++i){
        for(int j=0; j<n; ++j){
            if(a[i][j] != 0) continue;
            
            for(int d=0; d<4; ++d){
                int ny=i+dy[d], nx=j+dx[d];
                if(ny < 0 || ny >= n || nx < 0 || nx >= n) continue;
                if(0 != a[ny][nx]){
                    Distance[ny][nx] = min(Distance[ny][nx], 1);
                    q.push({i, j, a[ny][nx]});
                    break;
                }
            }
        }
    }
    
    int answer = 0;
    int dist = 0;
    while(!q.empty()){
        ++dist;
        int qs = q.size();
        while(qs--){
            int y = q.front().y;
            int x = q.front().x;
            int c = q.front().color;
            q.pop();
            
            if(a[y][x]) continue;
            a[y][x] = c;
            Distance[y][x] = dist;
            
            for(int d=0; d<4; ++d){
                int ny=y+dy[d], nx=x+dx[d];
                if(ny < 0 || ny >= n || nx < 0 || nx >= n) continue;
                
                if(a[ny][nx] == 0){
                    q.push({ny, nx, c});
                } else if(a[ny][nx] != c){
                    int expected = dist + Distance[ny][nx];
                    if(answer) answer = min(answer, expected);
                    else answer = expected;
                }
            }
        }
        if(answer > 0) break;
    }
    
    printf("%d", answer);
    
    return 0;
}

2016년 10월 27일 목요일

2873 - 롤러코스터

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

홀수x짝수, 짝수x홀수, 홀수x홀수 크기에 대해서는 모든 경로를 방문하여 가장 오른쪽 아래 칸으로 갈 수 있다.

짝수x짝수 크기는 1칸 빼고 나머지는 다 방문해서 도착할 수 있는데, (홀수, 짝수) 또는 (짝수, 홀수)에 위치한 칸을 들리지 못한다. 빼는 1칸은 가장 작은 기쁨이 있는 칸을 선택하면 될 것이다.

여기까지는 어떻게 생각해냈는데, 경로를 어떻게 만들어야할지 몰라서 한참 고민하다 결국 솔루션을 참고해버렸다.

경로를 크게 3부분으로 나눈다.


가운데 지그재그로 제외할 1칸을 피해서 이동하는 것 때문이다. 지그재그로 2줄만 어떻게 해결하면 되므로, 그 전행까지는 R...RDL...LD 로 이동하고, 이후의 행부터는 DL...LDR...R 로 이동하면 된다.

#include <cstdio>
using namespace std;

int H, W;
int a[1000][1000];

void repeat(char ch, int repeated){while(repeated-->0)putchar(ch);}

int main(){
    scanf("%d %d", &H, &W);
    
    for(int i=0; i<H; ++i){
        for(int j=0; j<W; ++j){
            scanf("%d", &a[i][j]);
        }
    }
    
    if( H%2 ){
        repeat('R', W-1);
        for(int i=0; i<H-1; i+=2){
            repeat('D', 1); repeat('L', W-1);
            repeat('D', 1); repeat('R', W-1);
        }
    } else if( W%2 ){
        repeat('D', H-1);
        for(int i=0; i<W-1; i+=2){
            repeat('R', 1); repeat('U', H-1);
            repeat('R', 1); repeat('D', H-1);
        }
    } else {
        // finding minimum cell
        int x=0, y=1;
        for(int i=0; i<H; ++i){
            for(int j=0; j<W; ++j){
                if((i+j)%2 && a[y][x] > a[i][j]){
                    y = i, x = j;
                }
            }
        }
    
        int h=0, w=0;
        // top
        for(; h+1 < y; h += 2){
            repeat('R', W-1); repeat('D', 1);
            repeat('L', W-1); repeat('D', 1);
        }
        
        // middle
        int ny = h^1;
        for(w=0; w < W-1; ++w){
            if( ny != y || w != x ){
                repeat( ny%2 ? 'D':'U', 1);
                ny ^= 1;
            }
            repeat('R', 1);
        }
        if( ny%2 ) repeat('D', 1);
        
        // bottom
        h = (h/2*2) + 2;
        for(; h < H; h += 2){
            repeat('D', 1); repeat('L', W-1);
            repeat('D', 1); repeat('R', W-1);
        }
    }
    
    return 0;
}

2016년 10월 26일 수요일

1541 - 잃어버린 괄호

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

음수 부호(-)가 나오는 순간부터 모든 수가 음수라고 생각하면 된다. a+b-(c+...+z) 가 되기 때문.

#include <bits/stdc++.h>
using namespace std;

int main() {
    char s[101];
    scanf("%s", s);
    int num=0, sum=0;
    bool sub = false;
    for(int i=0; s[i]; ++i){
        if('0' <= s[i] && s[i] <= '9'){
            num = 10*num + (s[i]-'0');
        } else {
            if(!sub) sum += num;
            else sum -= num;
            num = 0;
            sub |= s[i] == '-';
        }
    }
    if(!sub) sum += num;
    else sum -= num;
    
    printf("%d", sum);
    return 0;
}

1080 - 행렬

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

왼쪽에서 오른쪽으로, 위에서 아래로 한 칸씩 확인하면서 3x3크기의 부분행렬이 서로 다르다면 바꾼다. 마지막에 바꾼 횟수를 출력하면 정답이다.

이런 그리디가 통할 수 있는 것은, 이미 지나간 부분(탐색한 부분)에서 서로 다른 칸이 존재하면 안되기 때문이다.

#include <bits/stdc++.h>
using namespace std;

int h, w;
char A[50][51];

void flip(int y, int x){
    for(int i=y; i<y+3; ++i){
        for(int j=x; j<x+3; ++j){
            A[i][j] = !A[i][j];
        }
    }
}

int main(){
    scanf("%d %d", &h, &w);
    for(int i=0; i<h; ++i) scanf("%s ", A[i]);
    for(int i=0; i<h; ++i){
        char s[51];
        scanf("%s ", s);
        for(int j=0; j<w; ++j){
            A[i][j] = A[i][j] == s[j];
        }
    }
    
    int ans = 0;
    for(int i=0; i<h-2; ++i){
        for(int j=0; j<w-2; ++j){
            if( A[i][j] == 0 ){
                flip(i, j);
                ans += 1;
            }
        }
    }
    
    for(int i=0; i<h; ++i){
        for(int j=0; j<w; ++j)
            if(A[i][j] == 0) return puts("-1"), 0;
    }
    
    printf("%d\n", ans);
    
    return 0;
}

게시글 목록