[ARC080F]Prime Flip——贪心+最大二分匹配

18 篇文章 0 订阅
14 篇文章 0 订阅

题目大意:

有若干个01排成一行,每一个可以选择一个长度为奇质数的区间进行每个数字的翻转,问最小多少次可以将所有的1变成0。

思路:

考虑到是区间的操作,我们现将这个数组差分一下,即和前面的数不同为1,否则为0,这样区间操作就变成了两个点的操作。
显然我们要每次两个点两个点地改成0。考虑两个点之间的距离和操作数量的关系。

  1. 如果距离为奇质数的话,就只需要一次操作。
  2. 如果距离为偶数的就要两次操作。
  3. 如果距离为奇非质数的话,就会要三次操作。

贪心地选择进行哪种操作,即先满足第一种操作尽可能的多,再满足第二种操作尽可能的多。考虑这种做法的正确性,在满足第一种操作尽可能多的情况下,并不会造成第三种操作的增多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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值