eoj 3449

(二分+二分匹配)
题意:总共 n(n3000) 轮游戏,每一轮给定一个数(第 i 轮给出的数是 ai(ai106) )。同时每次要回答一个数 x x 是我给出的这个数的质因数,并且说出的数不能重复。求游戏能进行几轮?

思路:将模型转化为二分图的最大匹配来做,二分图左边为前 k 轮的数字,右边为这 k 个数字的所有质因子,然后二分 k 求解即可。(复习下二分匹配的板子)

代码:

#include <cstdio>
#include <set>
#include <vector>
#include <cstring>
#include <algorithm>
#define LL long long

using namespace std;
const int maxn = 3010;
const int maxm = 1000010;

int a[maxn];
vector<int> pri[maxn];
int head[maxn], cnt;
struct e{
    int to, next;
}edge[maxn<<3];
void init_graph(int n) { cnt = 0; memset(head, -1, sizeof(head[0])*(n+5)); }
void add_edge(int u, int v){
    edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt ++;
}

bool bmask[maxm];
int cx[maxn], cy[maxm];
int findpath(int u){
    for(int k=head[u]; k!=-1; k=edge[k].next){
        int i = edge[k].to;
        if(!bmask[i]){
            bmask[i] = 1;
            if(cy[i] == -1 || findpath(cy[i])){
                cy[i] = u;
                cx[u] = i;
                return 1;
            }
        }
    }
    return 0;
}

int MaxMatch(int nx, int ny){
    int ret = 0;
    memset(cx,-1,sizeof(cx[0])*(nx+5));
    memset(cy,-1,sizeof(cy[0])*(ny+5));
    for(int i=1; i<=nx; i++)
        if(cx[i] == -1){
            memset(bmask,0,sizeof(bmask[0])*(ny+5));
            ret += findpath(i);
        }
    return ret;
}

set<int> S;
set<int>::iterator iter;
vector<int> vec_y;
bool work(int n) {
    init_graph(n);
    S.clear();
    for(int i=1; i<=n; i++) {
        int sz = pri[i].size();
        for(int j=0; j<sz; j++)
            S.insert(pri[i][j]);
    }
    vec_y.clear();
    for(iter=S.begin(); iter!=S.end(); iter++)
        vec_y.push_back(*iter);
    for(int i=1; i<=n; i++) {
        int sz = pri[i].size();
        for(int j=0; j<sz; j++) {
            int k = lower_bound(vec_y.begin(), vec_y.end(), pri[i][j]) - vec_y.begin() + 1;
            add_edge(i, k);
        }
    }
    if(MaxMatch(n, (int)vec_y.size()) == n) return true;
    return false;
}


int main() {
    int n;
    while(scanf("%d",&n) == 1) {
        for(int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            pri[i].clear(); int t = a[i];
            for(int j=2; j*j<=t; j++) if(t%j == 0) {
                pri[i].push_back(j);
                while(t%j == 0) t /= j;
            }
            if(t > 1) pri[i].push_back(t);
        }
        int l = 1, r = n;
        while(l < r) {
            int mid = (l+r+1)/2;
            if(work(mid)) l = mid;
            else r = mid - 1;
        }
        printf("%d\n",l);
    }
    return 0;
}
发布了40 篇原创文章 · 获赞 44 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Site1997/article/details/78797481