알고리즘 정리

15 분 소요


</br> 알고리즘들을 정리하고 복습하자
</br>

알고리즘

Binary Search

// now finding 'num'
    int l = 0, h = n;
    int ans = 0;
    while( l <= h ){
        int mid = (l+h) / 2;

        // do something

        if( /*some*/ ){
            l = mid + 1;
            update(ans);
        }
        else h = mid - 1;
    }
    ans = l;

종료 조건이 다양하니까 그 때 그 때 다르게
</br>

stl

// now finding 'num'
    auto it = lower_bound(arr.begin(), arr.end(), num);

    if( binary_search(arr.begin(), arr.end(), num) ) 찾음
    else  찾음

그냥 찾기만 하면 된다면, stl로 O(logN) 사용 set, map에서 find()는 O(N)임
</br>

Two Pointer

    int l = 0, r = v.size()-1;
    while( l < r ){
        if( 조건 ){
            break;
        }
        else if( 조건 < w ) l++;
        else r--;
    }

정렬된 상태에서 양쪽의 값을 더해서 작으면 왼쪽 포인터를 오른쪽으로, 크면 오른쪽 포인터를 왼쪽으로

</br>

Floyd Warshall

    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);


</br>

Dijkstra

    for(int i = 1; i <= n; i++){
        dist[i] = INT_MAX;
    }
    priority_queue<P, vector<P>, greater<P>> pq;
    dist[start] = 0;
    pq.push(make_pair(0, start));
    while( pq.size() ){
        int v = pq.top().second;
        int w = pq.top().first;
        pq.pop();

        if( dist[v] < w ) continue;

        for(int i = 0; i < graph[v].size(); i++){
            int u = graph[v][i].second;
            if( dist[u] > w + graph[v][i].first ){
                dist[u] = w + graph[v][i].first;
                pq.push(make_pair(dist[u], u));
            }
        }
    }


</br>

Bellman Ford

    for(int i = 1; i <= n; i++){
        dist[i] = MAX;
    }
    dist[1] = 0;
    for(int i = 1; i <= n; i++){
        for(int j = 0; j < edges.size(); j++){
            int s = get<0>(edges[j]);
            int e = get<1>(edges[j]);
            int t = get<2>(edges[j]);
            if( dist[s] != MAX && dist[e] > dist[s] + t ){
                dist[e] = dist[s] + t;
                if( i == n ) return true;
            }
        }
    }
    return false;


</br>

Union Find

struct UnionFind{
	vector<int> parent, ran;
    UnionFind(int n) : parent(n+1), ran(n+1, 1){
    	for(int i = 1; i <= n; i++){
            parent[i] = i;
    	}
    }
    int f(int u){
    	if( u == parent[u] ) return u;
    	parent[u] = f(parent[u]);
        return parent[u];
    }
    bool merg(int u, int v){
    	u = f(u); v = f(v);
        if( u == v ) return false;
        if( ran[u] > ran[v] ) swap(u, v);
        parent[u] = v;
        ran[v] += ran[u];
        ran[u] = 0;
        return true;
    }
};


</br>

최소 신장 트리(MST)

Kruskal(Union Find)

    UnionFind uf = UnionFind(n);
    int ans = 0;
    while( pq.size() ){
        int c = get<0>(pq.top());
        int a = get<1>(pq.top());
        int b = get<2>(pq.top());
        pq.pop();
        if( uf.merg(a, b) ) ans += c;
    }
    cout << ans << endl;


</br>

Manachers Algorithm

    string s = "#";
    for(int i = 0; i < t.size(); i++){
        s.append(1, t[i]);
        s.append(1, '#');
    }
    for(int i = 0; i < s.size(); i++){
        int cnt = 0;
        for(int j = 1; j < s.size(); j++){
            if( i-j < 0 || s.size() <= i+j ) break;
            if( s[i-j] != s[i+j] ) break;
            cnt++;
        }
        manachers[i] = cnt;
    }


</br>

Topological Sort

    for(int i = 0; i < m; i++){
        int a, b;
        cin >> a >> b;
        v[a].push_back(b);
        ind[b]++;
    }
    queue<int> q;
    for(int i = 1; i <= n; i++){
        if( ind[i] == 0 ) q.push(i);
    }
    while( q.size() ){
        int now = q.front();
        q.pop();
        for(int i = 0; i < v[now].size(); i++){
            int nx = v[now][i];
            ind[nx]--;
            if( ind[nx] == 0 ) q.push(nx);
        }
    }

만약 인덱스가 빠른 순이면 priority queue 사용
</br>

Trie

vector<string> v;

struct Trie{
    bool fin;
    string val;
    map<string, Trie*> nodes;
    vector< pair<string, Trie*> > sortedNodes;
    Trie(string s){
        fin = false;
        val = s;
    }
    void Tinsert(int i){
        if( nodes.count(v[i]) == 0 ) nodes[v[i]] = new Trie(v[i]);
        if( v.size()-1 == i ) fin = true;
        else nodes[v[i]]->Tinsert(i+1);
    }
    void Tsort(){
        sortedNodes = vector< pair<string, Trie*> >(nodes.begin(), nodes.end());
        sort(sortedNodes.begin(), sortedNodes.end());
    }
    void Tprint(int d){
        Tsort();
        for(int i = 0; i < sortedNodes.size(); i++){
            for(int j = 0; j < d; j++){
                cout << "--";
            }
            cout << sortedNodes[i].first << endl;
            sortedNodes[i].second->Tprint(d+1);
        }
    }
};

// in main()
    Trie trie = Trie("");

    // push to vector
    trie.Tinsert(0);
    // clear vector

    trie.Tprint(0);


</br>

Segment Tree

구간 합

long long makeSeg(int a, int b, int now){
    if( a == b ) return segtree[now] = nums[a];
    int mid = (a+b) / 2;
    segtree[now] = makeSeg(a, mid, now*2) + makeSeg(mid+1, b, now*2+1);
    return segtree[now];
}

long long sumSeg(int a, int b, int now, int l, int r){
    if( r < a || b < l ) return 0;
    if( l <= a && b <= r ) return segtree[now];
    int mid = (a+b) / 2;
    return sumSeg(a, mid, now*2, l, r) + sumSeg(mid+1, b, now*2+1, l, r);
}

void updateSeg(int a, int b, int now, int ind, long long change){
    if( ind < a || b < ind ) return;
    segtree[now] += change;
    if( a == b ) return;
    int mid = (a+b) / 2;
    updateSeg(a, mid, now*2, ind, change);
    updateSeg(mid+1, b, now*2+1, ind, change);
}

// in main()
    int h = ceil(log2(n));
    segtree.assign(1<<(h+1), 0);
    makeSeg(0, n-1, 1);

    long long change = val - nums[b];
    updateSeg(0, n-1, 1, b, change);
    nums[b] = c;

    cout << sumSeg(0, n-1, 1, b, c) << '\n';

구간 곱

long long makeSeg(int a, int b, int now){
    if( a == b ) return segtree[now] = nums[a];
    int mid = (a+b) / 2;
    segtree[now] = makeSeg(a, mid, now*2) * makeSeg(mid+1, b, now*2+1);
    segtree[now] %= MAX;
    return segtree[now];
}

long long sumSeg(int a, int b, int now, int l, int r){
    if( r < a || b < l ) return 1;
    if( l <= a && b <= r ) return segtree[now];
    int mid = (a+b) / 2;
    long long s1 = sumSeg(a, mid, now*2, l, r) % MAX;
    long long s2 = sumSeg(mid+1, b, now*2+1, l, r) % MAX;
    return (s1 * s2) % MAX;
}

long long updateSeg(int a, int b, int now, int ind, long long val){
    if( ind < a || b < ind ) return segtree[now];
    if( a == b ) return segtree[now] = val;
    int mid = (a+b) / 2;
    long long u1 = updateSeg(a, mid, now*2, ind, val);
    long long u2 = updateSeg(mid+1, b, now*2+1, ind, val);
    return segtree[now] = (u1*u2) % MAX;
}


</br>

좌표 압축

    for(int i = 1; i <= n; i++){
        cin >> v[i];
        comp.push_back(v[i]);
    }
    sort(comp.begin(), comp.end());
    comp.erase(unique(comp.begin(), comp.end()), comp.end());
    for(int i = 1; i <= n; i++){
        v[i] = lower_bound(comp.begin(), comp.end(), v[i]) - comp.begin() + 1;
    }


</br>

LCA, 두 정점 사이 거리

vector<P> tree[40001];
int depth[40001];
int ac[40001][16];
int dist[40001];
int max_level;

void getTree(int now, int pre, int d){
    depth[now] = depth[pre] + 1;
    ac[now][0] = pre;
    dist[now] = d;

    for(int i = 1; i <= max_level; i++){
        int tmp = ac[now][i - 1];
        ac[now][i] = ac[tmp][i - 1];
    }

    for(int i = 0; i < tree[now].size(); i++){
        int nd = tree[now][i].first;
        int nx = tree[now][i].second;
        if( nx != pre ) getTree(nx, now, d + nd);
    }
}

int getlca(int a, int b){
    if( depth[a] != depth[b] ){
        if( depth[a] > depth[b] ) swap(a, b);
        for(int i = max_level; i >= 0; i--){
            if( depth[a] <= depth[ac[b][i]] ){
                b = ac[b][i];
            }
        }
    }
    int ret = a;
    if( a != b ){
        for(int i = max_level; i >= 0; i--){
            if( ac[a][i] != ac[b][i] ){
                a = ac[a][i];
                b = ac[b][i];
            }
            ret = ac[a][i];
        }
    }
    return ret;
}

// in main()

    max_level = (int)floor(log2(n));

    for(int i = 0; i < n-1; i++){
        int a, b, c;
        cin >> a >> b >> c;
        tree[a].push_back(make_pair(c, b));
        tree[b].push_back(make_pair(c, a));
    }

    depth[0] = -1;
    getTree(1, 0, 0);


    // 두 정점 사이 거리
        int lca = getlca(a, b);
        dist[a] + dist[b] - 2*dist[lca]


</br>

KMP

    int psz = p.size();
    vector<int> pi(psz, 0);
    int start = 1, matched = 0;
    while( start + matched < psz ){
        if( p[start + matched] == p[matched] ){
            matched++;
            pi[start + matched - 1] = matched;
        }
        else{
            if( matched == 0 ) start++;
            else{
                start += matched - pi[matched - 1];
                matched = pi[matched - 1];
            }
        }
    }

    vector<int> ans;
    matched = 0;
    for(int i = 0; i < t.size(); i++){
        while( matched > 0 && t[i] != p[matched] ){
            matched = pi[matched - 1];
        }
        if( t[i] == p[matched] ){
            matched++;
            if( matched == psz ){
                ans.push_back(i - psz + 2);
                matched = pi[matched - 1];
            }
        }
    }


</br>

LIS

이분탐색

pair<int, int> line[100001];
pair<int, int> tracking[100001];
vector<int> ind;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n;
    cin >> n;
    for(int i = 0; i < n; i++){
        int a, b;
        cin >> a >> b;
        line[i] = make_pair(a, b);
    }

    sort(line, line + n);

    ind.push_back(line[0].second);
    tracking[0] = make_pair(1, line[0].first);
    for(int i = 1; i < n; i++){
        int a = line[i].first;
        int b = line[i].second;
        if( b > ind.back() ){
            ind.push_back(b);
            tracking[i] = make_pair(ind.size(), a);
        }
        else{
            auto it = lower_bound(ind.begin(), ind.end(), b);
            *it = min(*it, b);
            tracking[i] = make_pair(it-ind.begin()+1, a);
        }
    }

    cout << ind.size() << '\n';

    stack<int> st;
    int t = ind.size();
    for(int i = n-1; i >= 0; i--){
        if( tracking[i].first == t ){
            st.push(tracking[i].second);
            t--;
        }
    }
    while( st.size() ){
        cout << st.top() << '\n';
        st.pop();
    }
}


</br>

세그먼트 트리

int n;
pair<int, int> v[1000001];
vector<int> segtree;

int maxSeg(int a, int b, int now, int l, int r){
    if( r < a || b < l ) return 0;
    if( l <= a && b <= r ) return segtree[now];
    int mid = (a+b) / 2;
    return max(maxSeg(a, mid, now*2, l, r), maxSeg(mid+1, b, now*2+1, l, r));
}

int updateSeg(int a, int b, int now, int ind, int change){
    if( ind < a || b < ind ) return 0;
    if( a == b ) return segtree[now] = change;
    int mid = (a+b) / 2;
    return segtree[now] = max({segtree[now], updateSeg(a, mid, now*2, ind, change), updateSeg(mid+1, b, now*2+1, ind, change)});
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> n;
    for(int i = 0; i < n; i++){
        int a;
        cin >> a;
        v[i] = make_pair(a, i);
    }
    sort(v, v + n, [](pair<int, int> p, pair<int, int> q) {
		if (p.first != q.first) return p.first < q.first;
		return p.second > q.second;
	});

    int h = ceil(log2(n));
    segtree.assign(1<<(h+1), 0);

    stack< pair<int, int> > st;
    for(int i = 0; i < n; i++){
        int a = maxSeg(0, n-1, 1, 0, v[i].second) + 1;
        updateSeg(0, n-1, 1, v[i].second, a);
        st.push(make_pair(a, v[i].first));
    }
    int m = maxSeg(0, n-1, 1, 0, n-1);
    cout << m << endl;

    stack<int> st2;
    m++;
    while( st.size() ){
        while( st.size() && st.top().first != m-1 ){
            st.pop();
        }
        st2.push(st.top().second);
        m = st.top().first;
        if( m == 1 ) break;
    }

    while( st2.size() ){
        cout << st2.top() << ' ';
        st2.pop();
    }
}


</br>

DAG에서 최장 경로

vector<P> graph[10001];
vector<P> revgraph[10001];
int ind[10001];
int times[10001];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;
    cin >> n >> m;
    for(int i = 0; i < m; i++){
        int a, b, c;
        cin >> a >> b >> c;
        graph[a].push_back(make_pair(c, b));
        revgraph[b].push_back(make_pair(c, a));
        ind[b]++;
    }
    int start, arrival;
    cin >> start >> arrival;

    queue<int> q;
    q.push(start);
    while( q.size() ){
        int now = q.front();
        q.pop();
        for(int j = 0; j < graph[now].size() ; j++){
            int c = graph[now][j].first;
            int nx = graph[now][j].second;
            ind[nx]--;
            times[nx] = max(times[nx], times[now] + c);
            if( ind[nx] == 0 ) q.push(nx);
        }
    }
    cout << times[arrival] << '\n';

    q.push(arrival);
    ind[arrival] = 1;
    int cnt = 0;
    while( q.size() ){
        int now = q.front();
        q.pop();

        if( now == start ) break;

        for(int j = 0; j < revgraph[now].size() ; j++){
            int c = revgraph[now][j].first;
            int nx = revgraph[now][j].second;
            if( times[now] - c == times[nx] ){
                cnt++;
                if( ind[nx] == 0 ){
                    ind[nx] = 1;
                    q.push(nx);
                }
            }
        }
    }
    cout << cnt << '\n';
}


</br>

SCC

vector<int> graph[10001];
vector<int> revgraph[10001];
int visited[10001];
priority_queue<P, vector<P>, less<>> pq;
vector< vector<int> > ans;
int d;

void func(int now){
    for(int i = 0; i < graph[now].size(); i++){
        int nx = graph[now][i];
        if( visited[nx] == 0 ){
            visited[nx] = 1;
            d++;
            func(nx);
        }
    }
    d++;
    pq.push(make_pair(d, now));
}

void func2(int now, int ind){
    for(int i = 0; i < revgraph[now].size(); i++){
        int nx = revgraph[now][i];
        if( visited[nx] == 0 ){
            visited[nx] = 1;
            ans[ind].push_back(nx);
            func2(nx, ind);
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int v, e;
    cin >> v >> e;
    for(int i = 0; i < e; i++){
        int a, b;
        cin >> a >> b;
        graph[a].push_back(b);
        revgraph[b].push_back(a);
    }

    d = 1;
    for(int i = 1; i <= v; i++){
        if( visited[i] ) continue;
        visited[i] = 1;
        func(i);
    }

    for(int i = 1; i <= v; i++){
        visited[i] = 0;
    }
    int ind = 0;
    while( pq.size() ){
        int now = pq.top().second;
        pq.pop();

        if( visited[now] ) continue;

        vector<int> t = {now};
        ans.push_back(t);
        visited[now] = 1;
        func2(now, ind);
        ind++;
    }
}


</br>

Inversion 개수 세기

Merge Sort

int n;
int nums[500001], sorted[500001];
long long cnt;

void myMerge(int l, int mid, int r){
	int a = l, b = mid + 1, k = l;
	while(a <= mid && b <= r){
		if( nums[a] <= nums[b] ) sorted[k++] = nums[a++];
		else{
			cnt += (mid - a + 1);
			sorted[k++] = nums[b++];
		}
	}
	if( a <= mid ){
		for(int i = a; i <= mid; i++){
            sorted[k++] = nums[i];
		}
	}
	else{
		for(int i = b; i <= r; i++){
            sorted[k++] = nums[i];
		}
	}
	for(int i = l; i <= r; i++){
        nums[i] = sorted[i];
	}
}

void mergeSort(int l, int r){
	if( l < r ){
		int mid = (l + r) / 2;
		mergeSort(l, mid);
		mergeSort(mid + 1, r);
		myMerge(l, mid, r);
	}
}

// in main()
    mergeSort(1, n);
    cout << cnt << endl;


</br>

Bipartite Matching

vector<int> v[1001];
int work[1001];
int visited[1001];

bool dfs(int now){
    visited[now] = 1;

    for(int i = 0; i < v[now].size(); i++){
        int nx = v[now][i];
        if( work[nx] == 0 || (visited[work[nx]] == 0 && dfs(work[nx])) ){
            work[nx] = now;
            return true;
        }
    }
    return false;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        int a;
        cin >> a;
        for(int j = 0; j < a; j++){
            int b;
            cin >> b;
            v[i].push_back(b);
        }
    }

    int ans = 0;
    for(int i = 1; i <= n; i++){
        for(int k = 0; k < 매칭가능한 최대 개수; k++){
            for(int j = 0; j < n; j++){
                visited[j] = 0;
            }
            if( dfs(i) ) ans++;
        }
    }

    cout << ans << endl;

}


</br>

수식

경우의 수

조합

nCr[i][j] = n! / (r! * (n-r)!);

nCr[i][j] = nCr[i-1][j-1] + nCr[i-1][j];

(a+b)^n = nCr[n][0]*a^0*b^n + nCr[n][1]*a^1*b^n-1 + ... + nCr[n][n]*a^n*b^0

nCr[n][0] + nCr[n][1] + ... + nCr[n][n] = 2^n
nCr[n][0] + nCr[n][2] + ... = 2^(n-1)

같은 것이 있는 순열

ex) aaabb => 5! / (3! * 2!), 최단거리 길찾기
n! / (an! * bn! * ... * zn!)


</br>

중복 조합

nHr = (n+r-1)Cr


</br>

GCD

int gcd(int a, int b){
    if( b > a ) return gcd(b, a);
    if( a%b == 0 ) return b;
    return gcd(b, a%b);
}


</br>

피보나치

| Fn+1 Fn   |       | 1 1 |
| Fn   Fn-1 |       | 1 0 |

행렬 곱으로 나타내기

O(logN)

struct F{
    long long a, b, c, d;
};
F one = F{1, 1, 1, 0};

F func(F f1, F f2){
    F t;
    t.a = f1.a * f2.a + f1.b * f2.c;
    t.b = f1.a * f2.b + f1.b * f2.d;
    t.c = f1.c * f2.a + f1.d * f2.c;
    t.d = f1.c * f2.b + f1.d * f2.d;
    t.a %= 1000000007;
    t.b %= 1000000007;
    t.c %= 1000000007;
    t.d %= 1000000007;
    return t;
}

F fibo(F f, long long d){
    if( d == 1 ) return one;
    if( d % 2 ) return func(fibo(f, d-1), one);
    F t = fibo(f, d/2);
    return func(t, t);
}

// in main()
    if( n == 0 ) cout << 0 << '\n';
    else if( n == 1 ) cout << 1 << '\n';
    else{
        F f = F{1, 1, 1, 0};
        f = fibo(f, n);
        cout << f.b % 1000000007 << endl;
    }


</br>

빠른 pow

long long mypow(long long a, int b){
    if( b == 0 ) return 1;
    if( b == 1 ) return a;
    if( b % 2 ) return a*mypow(a, b-1) % MOD;
    long long aa = mypow(a, b/2) % MOD;
    return aa*aa % MOD;
}


</br>

모듈러 곱셈의 역원

a^(MOD-2) = a 곱셈의 역원


</br>

소인수분해

    for(long long i = 2; i*i <= n; i++){
        long long cnt = 0;
        while( n % i == 0 ){
            n /= i;
            cnt++;
        }
        primes[i] = cnt;
    }
    if( n - 1 ) primes[n-1] = 1;


</br>

오일러 파이(서로소의 개수)

phi(n) = (A^a - A^(a-1)) * (B^b - B^(b-1)) * ... * (Z^z - Z^(z-1))
       = A^(a-1)*(A-1) * B^(b-1)*(B-1) * ... * Z^(z-1)*(Z-1)

    for(long long i = 2; i*i <= n; i++){
        long long cnt = 0;
        while( n % i == 0 ){
            n /= i;
            cnt++;
        }
        if( cnt ){
            ans *= pow(i, cnt-1);
            ans *= i - 1;
        }
    }
    if( n - 1 ) ans *= n - 1;


</br>

Signed Area, CCW

다각형 넓이 구하기

(다각형을 이루는 순서대로일 )
| x1 x2 ... xn | > + x1*yn + x2*y1 + ... + xn*yn-1
| y1 y2 ... yn | > - x1*y2 - x2*y3 - ... - xn*y1
ans = sarea / 2;

교차 판단

int sarea(P a, P b, P c){
    ll x1 = a.first;
    ll y1 = a.second;
    ll x2 = b.first;
    ll y2 = b.second;
    ll x3 = c.first;
    ll y3 = c.second;
    ll ret = x2*y1 + x3*y2 + x1*y3 - (x1*y2 + x2*y3 + x3*y1);
    if( ret < 0 ) return 1;
    if( ret == 0 ) return 0;
    return -1;
}

bool isCross(P a, P b, P c, P d){
    int abc = sarea(a, b, c);
    int abd = sarea(a, b, d);
    int cda = sarea(c, d, a);
    int cdb = sarea(c, d, b);

    if( abc*abd <= 0 && cda*cdb <= 0 ){
        if( abc*abd == 0 && cda*cdb == 0 ){
            if( a > b ) swap(a, b);
            if( c > d ) swap(c, d);
            if( a <= d && c <= b ) return true;
            else return false;
        }
        else return true;
    }
    else return false;
}


</br>

벌집 인덱스

1

</br>



</br>

댓글남기기