2017년 12월 27일 수요일

14672 윤호는 마법약 도둑

14672 윤호는 마법약 도둑 https://www.acmicpc.net/problem/14672

윤호는 M개의 N보다 작은 숫자가 적혀져있는 마법약들을 가지고 있다.
마법약은 1을 제외한 약수들중 하나로 추출 할 수 있다.
추출에 성공하려면 추출한 제품들 중에서 어떠한 원료도 공유해서는 안된다.
윤호의 추출할 수 있는 최대 마법약의 개수를 구하는 문제이다.

마법약들을 추출했을 때 모든 마법약들의 원료들이 서로소인 최대 마법약 개수를 구하는 것이다.
gcd를 이용하기에는 구할 방법이 마땅히 없어보였다.

사실상 원료들이 서로소가 되려면 마법약에서 추출할 수 원료들을 소수로 추출하면 된다.
문제는 최대 마법약 개수를 구해야하는데 이분매칭을 이용하면 된다.
네트워크를 나타내면 위와같이 될것이다.
N이 100,000,000이므로 최대 10,000까지의 소수만 구해주면 되는데 여기 실수가 있었다.

10,000보다 큰 수가 들어올 때 10,000보다 작은 소수로 소인수 분해를 하더라도 끝나지 않을 수 있다.
예를들어 104,729와 같은 수이다. 

이런 수는 따로 처리를 해서 저장을 해줘야한다.
밑의 코드에서 소인수 분해를 하고 x > 1인경우가 따로 처리한 경우이다.
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
#define MAX 3500
#define PLUS 2300
#define INF 987654321
int N, M;
int arr[1001];
int pr[10011];
vector<int> prime;
int Size;
vector<int> adj[MAX];
int C[MAX][MAX], f[MAX][MAX];
const int src = MAX - 2, sink = MAX - 1;
int level[MAX], work[MAX];
unordered_map<intint> save;
int cnt = 0;
void init() {
    int L = (int)sqrt(N) + 1;
    for (int n = 2;n*<= L;n++) {
        for (int m = n*n;m <= L;m += n) pr[m] = true;
    }
    for (int n = 2;n <= L;n++if (!pr[n]) prime.push_back(n);
    Size = prime.size();
}
void add_edge(int u, int v, int c) {
    adj[u].push_back(v);
    adj[v].push_back(u);
    C[u][v] = c;
}
bool bfs() {
    memset(level, -1sizeof level);
    queue<int> q;
    q.push(src);
    level[src] = 0;
    while (!q.empty()) {
        int here = q.front();
        q.pop();
        for (auto &next : adj[here]) {
            if (level[next] == -1 && C[here][next] - f[here][next] > 0) {
                level[next] = level[here] + 1;
                q.push(next);
            }
        }
    }
    return level[sink] != -1;
}
int dfs(int here, const int sink, int flow) {
    if (here == sink) return flow;
    for (int &= work[here]; n < adj[here].size(); n++) {
        int next = adj[here][n];
        if (level[next] == level[here] + 1 && C[here][next] - f[here][next] > 0) {
            int get = dfs(next, sink, min(flow, C[here][next] - f[here][next]));
            if (get) {
                f[here][next] += get;
                f[next][here] -= get;
                return get;
            }
        }
    }
    return 0;
}
int main() {
    scanf("%d%d"&N, &M);
    init();
    for (int m = 0, x;m < M;m++) {
        scanf("%d"&x);
        for (int n = 0;n < Size;n++) {
            if (x % prime[n] == 0) {
                add_edge(n, m + PLUS, 1);
                while (x && x % prime[n] == 0) x /= prime[n];
            }
        }
        if (x > 1) {
            if (save[x] == 0) save[x] = ++cnt;
            add_edge(Size + save[x], m + PLUS, 1);
        }
        add_edge(m + PLUS, sink, 1);
    }
    for (int n = 0;n < Size;n++) add_edge(src, n, 1);
    for (int n = 1;n <= cnt;n++) add_edge(src, Size + n, 1);
    int ans = 0;
    while (bfs()) {
        memset(work, 0sizeof work);
        while (1) {
            int get = dfs(src, sink, INF);
            if (!get) break;
            ans += get;
        }
    }
    printf("%d\n", ans);
    return 0;
}
cs

댓글 없음:

댓글 쓰기