Prime Flip

题目描述

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

输入

Input is given from Standard Input in the following format:
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;
}

  

猜你喜欢

转载自www.cnblogs.com/zyf3855923/p/9363240.html