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; }