您的位置:首页 > 其它

zoj 3988 Prime Set 【二分图最大匹配 + 思维】

2017-11-07 22:25 337 查看
//这可是秦皇岛的金牌题啊, 哎 , 当时没有时间来搞这个题目了, 否则感觉可以出的!!! 可惜啊.

传送门

//题意: 给你n个正整数, 一对和为素数的数为一个合法数对. 你选不超过K个合法数对, 使得你选的数对涉及到的数的数量最大化. 输出这个值.

思路: 通过观察可以发现, 我们可以先筛素数2e6以内的, 然后对这n个数进行建边, 符合条件的就有一条边, 然后跑二分图最大匹配, 那么结果就是如果这个最大匹配 >= K 了,那么直接输出2*k, 否则答案就是 最大匹配数*2 + min(k-最大匹配数, 剩余没有匹配过的但有边的点)(因为这些点可以对匹配过了的偶数进行复用, 使之到达k个集合, 并且可以加入新的元素, 这样才能使答案最优!!!) 具体细节请看代码!!!

AC Code

const int maxn = 3e3+5;
const int maxp = 2e6+5;
bool nopri[maxp];
void cal()
{
nopri[1] = 1;
for(int i=2;i<maxp-2;i++){
if(!nopri[i]){
for(int j=i*2;j<maxp-2;j+=i)
nopri[j] = 1 ;
}
}
}
int n,k;
vector<int>ve[maxn];
bool vis[maxn];
int link[maxn],a[maxn];
bool Find(int x)
{
vis[x] = 1;    //记得双向标记!!!
for(int i=0;i<ve[x].size();i++){
int tmp = ve[x][i];
if(vis[tmp]) continue;
vis[tmp] = true;
if(!link[tmp] || Find(link[tmp])){
link[tmp] = x;
link[x] = tmp;
return true;
}
}
return false;
}
void solve()
{
Fill(link,0);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ve[i].clear();
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(!nopri[a[i]+a[j]]){
ve[i].push_back(j);
ve[j].push_back(i);
}
}
}
int sum1 = 0,sum2 = 0;
for(int i=1;i<=n;i++){
Fill(vis,0);
if(!link[i] && Find(i))
sum1++; //最大匹配数
}

for(int i=1;i<=n;i++){
if(ve[i].size()!=0 && !link[i])
sum2++; //剩余最多可以加入的新元素的个数.
}

if(sum1>=k) cout << k*2 << endl;
else cout << sum1*2+min(k-sum1,sum2) << endl;
}
int main()
{
cal();  //预处理
int t = 1 ;
scanf("%d",&t);
while(t--){
solve();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: