题目描述
There are infinitely many cards, numbered 1, 2, 3, … Initially, Cards x1, x2, …, xN are face up, and the others are face down.
Snuke can perform the following operation repeatedly:
Select a prime p greater than or equal to 3. Then, select p consecutive cards and flip all of them.
Snuke's objective is to have all the cards face down. Find the minimum number of operations required to achieve the objective.
Constraints
1≤N≤100
1≤x1<x2<…<xN≤107
Snuke can perform the following operation repeatedly:
Select a prime p greater than or equal to 3. Then, select p consecutive cards and flip all of them.
Snuke's objective is to have all the cards face down. Find the minimum number of operations required to achieve the objective.
Constraints
1≤N≤100
1≤x1<x2<…<xN≤107
输入
Input is given from Standard Input in the following format:
N
x1 x2 … xN
N
x1 x2 … xN
输出
Print the minimum number of operations required to achieve the objective.
样例输入
2
4 5
样例输出
2
提示
Below is one way to achieve the objective in two operations:
Select p=5 and flip Cards 1, 2, 3, 4 and 5.
Select p=3 and flip Cards 1, 2 and 3.
这题难度感觉很大。
首先要把问题抽象。我们设如果第i张牌如果与第i-1张牌面向不同,ai=1,否则为0.特别的,a0=0。这样有什么好处呢。
这样我们动形如1000001的序列时,翻动100000。会得到什么效果?首先第一个1会变成0(与前一个变为同向),而其他的0由于与其前一位同时反转,仍旧是0,而后面的1,会变成0.这样就变成了两点操作。
则最终目标变为:求最小操作数,使序列全为0.
考虑三种情况
A.|i-j|为素数:操作一次
B.|i-j|为偶数,哥德巴赫猜想,分解成两个素数,操作两次。
C.|i-j|为奇合数。分解成偶数减去奇素数,操作三次。
我们把位置为奇数的点,位置为偶数的点分为两组,显然要操作1最多。
则想到对位置之差为素数的两点连边,跑二分图。
匈牙利即可。
注意匈牙利时,不能为以0为点,不然会有问题。因为这个wa了好多发
#include<bits/stdc++.h> #define maxn 10000005 #define maxv 205 using namespace std; int prime[maxn/2]; bool vis[maxn]; int inde=0; void primejudge(int n)//预处理素数筛 { vis[1]=true; int i,j; for(i=2;i<=n;i++) { if(!vis[i]) { prime[inde++]=i; } for(j=0;j<inde&&prime[j]*i<=n;j++) { vis[i*prime[j]]=true; if(i%prime[j]==0) break; } } vis[2]=true; } int cnt1,cnt2; bool used[maxv]; int bel[maxv]; bool v[maxn]; int s[maxn]; int mp1[maxv]; int mp2[maxv]; bool match[maxv][maxv]; bool findd(int x)//匈牙利算法跑二分图 { int i; for(i=1;i<=cnt2;i++) { if(match[x][i]&&used[i]==false) { used[i]=true; if(!bel[i]||findd(bel[i])) { bel[i]=x; return true; } } } return false; } int main() { int n,i,u,cnt=0; primejudge(10000003); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&u); v[u]=true;//存初始为翻开的点 } for(i=1;i<=maxn-2;i++) { if(v[i]!=v[i-1]) { s[cnt++]=i;//把为权为1的点的位置记录下来 } } for(i=0;i<cnt;i++) { if(s[i]&1) { mp1[++cnt1]=s[i];//奇数为一组 } else { mp2[++cnt2]=s[i];//偶数为一组 } } for(i=1;i<=cnt1;i++) { for(int j=1;j<=cnt2;j++) { if(!vis[abs(mp1[i]-mp2[j])]) { match[i][j]=true;//如果是素数,则在二分图中建边 } } } int sum=0; for(i=1;i<=cnt1;i++)//跑二分图 { memset(used,false,sizeof(used)); if(findd(i)) sum++; } int ans=sum; cnt1-=sum;//剩下没法匹配为素数的的肯定是cnt1-sum cnt2-=sum; ans+=cnt1/2*2;//剩下的内部匹配 ans+=cnt2/2*2; ans+=cnt1%2*3;//余数肯定是与另一组的单独匹配 cout<<ans<<endl; return 0; }