Description
有无限多的硬币排成一排,其中有一些位置的的硬币是反面的,其他的都是正面的,
现在你每次可以翻转一个区间的所有硬币,要求区间的长度必须为奇质数,
求最少操作次数,
Solution
先把原序列差分,每次就只会更改两个点,
分以下的几种情况讨论:
1. 区间长度为奇质数,代价为1;
2. 区间长度为偶数,代价为2(2:5-3, 4:7-3,大于6的用哥德巴赫猜想);
3. 区间长度为非质数奇数,代价为3(随便一个偶数-3),
以偶了以上的结论,贪心的想,尽量把要做的点两两匹配成奇质数,剩下的用偶数匹配,最后实在不行就补一个奇数的,
匹配用匈牙利即可
Code
#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
const int N=350,M=1e7;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
bool az[M+10],prz[M+10];
bool B[N][N];
int pr[M/5];
int a[N],b[N],zx[N];
int z[N],TI;
void Pre()
{
fo(i,2,M)
{
if(!prz[i])pr[++pr[0]]=i;
fo(j,1,pr[0])
{
int t=i*pr[j];
if(t>M)break;
prz[t]=1;
if(!t%pr[j])break;
}
}
prz[1]=prz[2]=1;
}
bool OK(int q)
{
if(z[q]==TI)return 0;
z[q]=TI;
fo(i,1,b[0])if(B[q][i]&&(!zx[i]||OK(zx[i])))return zx[i]=q,1;
return 0;
}
int main()
{
int q;
Pre();
read(n);
fo(i,1,n)az[read(q)]=1;
fo(i,1,M+1)if(az[i]!=az[i-1])
{
if(i&1)a[++a[0]]=i;
else b[++b[0]]=i;
}
fo(i,1,a[0])fo(j,1,b[0])if(!prz[abs(a[i]-b[j])])B[i][j]=1;
q=0;
fo(i,1,a[0])
{
++TI;if(OK(i))++q;
}
ans=q+2*((a[0]-q)/2+(b[0]-q)/2);
if((a[0]-q)&1)ans+=3;
printf("%d\n",ans);
return 0;
}