• 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;
    }
    

      

  • 相关阅读:
    多个在线参考手册,值得收藏
    DIV层,点“+”展开,“-”关闭
    网页设计标准尺寸
    【转】 大年三十整理的asp.net资料! (.NET) (ASP.NET)
    【转】xml操作
    【配色】web2.0 配色参考
    【转】关闭开机硬盘自检的方法
    【转】css的一些基础的东西
    web.config加密解密
    [转]用 Javascript 获取滚动条位置等信息
  • 原文地址:https://www.cnblogs.com/zyf3855923/p/9363240.html
Copyright © 2020-2023  润新知