题目大意:
有若干个01排成一行,每一个可以选择一个长度为奇质数的区间进行每个数字的翻转,问最小多少次可以将所有的1变成0。
思路:
考虑到是区间的操作,我们现将这个数组差分一下,即和前面的数不同为1,否则为0,这样区间操作就变成了两个点的操作。
显然我们要每次两个点两个点地改成0。考虑两个点之间的距离和操作数量的关系。
- 如果距离为奇质数的话,就只需要一次操作。
- 如果距离为偶数的就要两次操作。
- 如果距离为奇非质数的话,就会要三次操作。
贪心地选择进行哪种操作,即先满足第一种操作尽可能的多,再满足第二种操作尽可能的多。考虑这种做法的正确性,在满足第一种操作尽可能多的情况下,并不会造成第三种操作的增多1以上,因为无论在1操作中配对了多少,剩下的都是x个奇数点+2n-x个偶数点,奇数点偶数点之间可以自己配对形成第二种操作使得第三种操作的最大数量为1。
将点按照奇偶分开之后构建二分图大概就是这样的。
最后就是二分图最大匹配和判断一下奇偶了。
/*=======================================
* Author : ylsoi
* Problem : ARC80F
* Algorithm : Graph Matching and Greed
* Time : 2018.7.16
* ===================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("ARC80F.in","r",stdin);
freopen("ARC80F.out","w",stdout);
}
const int maxn=500+10;
int n,a[maxn],pos[maxn],tot,cnt,ans;
int be[maxn],cnte,to[maxn*maxn],las[maxn*maxn],beg[maxn];
bool vis[maxn];
void add(int u,int v){
las[++cnte]=beg[u];
beg[u]=cnte;
to[cnte]=v;
}
bool pd(int x){
if(x==1 || x==2)return false;
int y=sqrt(x);
REP(i,2,y)if(x%i==0)return false;
return true;
}
bool Hungary(int u){
for(int i=beg[u];i;i=las[i]){
if(vis[to[i]])continue;
vis[to[i]]=1;
if(!be[to[i]] || Hungary(be[to[i]])){
be[to[i]]=u;
return true;
}
}
return false;
}
int main(){
File();
scanf("%d",&n);
REP(i,1,n){
scanf("%d",&a[i]);
if(i==1 || a[i-1]<a[i]-1)pos[++tot]=a[i];
if(i!=1 && a[i]>a[i-1]+1)pos[++tot]=a[i-1]+1;
}
pos[++tot]=a[n]+1;
REP(i,1,tot)if(pos[i]%2)++cnt;
REP(i,1,tot)if(pos[i]%2==0){
REP(j,1,tot)if(pos[j]%2==1 && pd(abs(pos[j]-pos[i])))
add(i,j);
}
REP(i,1,tot)if(pos[i]%2==0){
memset(vis,0,sizeof(vis));
ans+=Hungary(i);
}
printf("%d\n",ans+(tot-ans*2)+(cnt-ans)%2);
return 0;
}