zoj3988(自己集合和自己集合匹配)

题:https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370128

题意:给定n个数(n<=3e3)俩者能匹配的充分必要条件未俩者之和为质数,求至多匹配完k对后各个数所属下标的并集最大是多少;

分析:二分图直接匈牙利算法搞起,主要k限制,那就判断匹配数和k之间关系;

   若ans<=k,那么匹配数足以贪心地输出2*k;

   否则看剩下有多少个,1个就有一个贡献,因为经过最大匹配后另一个一定被匹配完了;

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int M=3e3+5;
typedef long long ll;
#define pb push_back

const int N=2e6+6;
const int inf=0x3f3f3f3f;
int mp[M],vis[M],a[M];
int n;
int prime[N];
vector<int>g[M];
void init(){
    for(int i=2;i<N;i++){
        if(!prime[i]){
            for(int j=i+i;j<N;j+=i)
                prime[j]=1;
        }
    }
}
int dfs(int u){
    vis[u]=1;
    for(auto v:g[u]){
        if(!vis[v]){
            vis[v]=1;
            if(mp[v]==-1||dfs(mp[v])){
                mp[v]=u;
                mp[u]=v;
                return 1;
            }
        }
    }
    return 0;
}
int xiong(){
    int res=0;
    for(int i=1;i<=n;i++)
        if(mp[i]==-1){///一般的匈牙利算法不用这句,这题加代表选过了不能再选,只能从未选过的开始选
            memset(vis,0,sizeof(vis));
            res+=dfs(i);
        }
    return res;
}
int main(){
    int T;
    scanf("%d",&T);
    init();
    while(T--){
        int k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),mp[i]=0,g[i].clear();
        int sum=0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++)
                if(!prime[a[i]+a[j]]){
                    ///cout<<i<<"  "<<j<<endl;
                    g[i].pb(j),g[j].pb(i);
                    mp[i]=-1,mp[j]=-1;
                }
        }

        int ans=xiong();
        if(ans>=k){ printf("%d\n",k*2); }
        else{
            int leave=0;
            for(int i=1;i<=n;i++)
                if(mp[i]==-1) leave++;
            printf("%d\n",2*ans+min(k-ans,leave));
        }
    }
    return 0;
}
View Code

 

posted @ 2020-10-22 17:02  starve_to_death  阅读(138)  评论(0编辑  收藏  举报