博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Atcoder】ARC 080 F - Prime Flip
阅读量:6799 次
发布时间:2019-06-26

本文共 2567 字,大约阅读时间需要 8 分钟。

【算法】数论,二分图最大匹配

【题意】有无限张牌,给定n张面朝上的牌的坐标(N<=100),其它牌面朝下,每次操作可以选定一个>=3的素数p,并翻转连续p张牌,求最少操作次数使所有牌向下。

【题解】

1.定义bi,当ai和ai-1的朝向相同时,bi=0,否则bi=1。特别的,a0朝向下。

则问题转化为:给定01序列b[],每次选L(正数)和P(奇素数),翻转bL和bP,求最少操作次数使序列全0。

 

这么转化的关键在于差分,对于区间翻转,区间内的点bi都不会变化,只有区间左端和区间右端+1变化,将区间差分为两点

如此每次操作变成了对两点操作!大大简化了题目。

 

2.由于每次只能翻转2个数,考虑所有1之间的两两关系

若|i-j|是一个奇素数,操作次数为1。

若|i-j|是一个偶数,操作次数为2。(不严格,此处的意思是一个偶数可以分解为两个奇素数的加减结果)

若|i-j|是一个奇合数(或1),操作次数为3。(分解为偶数减奇素数的差)

 

素数与合数的关系通过手算小数据就可以得出,然后大胆推广到自然数范围内。

1的总数是偶数?证明:对于a[]中的一段1,b[]对应有2个1。

为什么每次处理两个1是最优的?感性理解:另外把0翻转成1没有收益,把1翻转成0不满足操作。

 

3.按照每个1所在位置是奇数和偶数分成两组m1,m2,同组内配对是偶数,不同组配对是奇数,定义k为两组配对产生的奇素数的对数。

ans=k*1+(m1-k)/2*2+(m2-k)/2*2+(m1-k)%2*3。

k为1次,剩余的组内配对为2次,最后各组有剩1个再配对为3次。

显然k要尽可能大,对两组建立二分图,差为奇素数连边,跑二分图最大匹配即可。

最坏复杂度O(n^3)。

#include
#include
#include
#include
using namespace std;const int maxn=510,maxL=10000010,inf=0x3f3f3f3f;struct cyc{
int v,flow,from;}e[maxn*maxn*2];int tot=1,cnt,n,first[maxn],d[maxn],S,T,num[maxn],b[maxn],cur[maxn];bool c[maxL];inline int ab(int x){
return x>0?x:-x;}bool isprime(int x){ if(x<=2)return 0; for(int i=2;i*i<=x;i++)if(x%i==0)return 0; return 1;}void insert(int u,int v,int w){ tot++;e[tot].v=v;e[tot].flow=w;e[tot].from=first[u];first[u]=tot; tot++;e[tot].v=u;e[tot].flow=0;e[tot].from=first[v];first[v]=tot;}queue
q;bool bfs(){ memset(d,-1,sizeof(d)); q.push(S);d[S]=0; while(!q.empty()){ int x=q.front();q.pop(); for(int i=first[x];i;i=e[i].from) if(d[e[i].v]==-1&&e[i].flow){ d[e[i].v]=d[x]+1; q.push(e[i].v); } } return d[T]!=-1;}int dinic(int x,int a){ if(x==T||a==0)return a; int f,flow=0; for(int& i=cur[x];i;i=e[i].from) if(d[e[i].v]==d[x]+1&&e[i].flow&&(f=dinic(e[i].v,min(a,e[i].flow)))>0){ e[i].flow-=f; e[i^1].flow+=f; a-=f; flow+=f; if(a==0)break; } return flow;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){scanf("%d",&num[i]);c[num[i]]=1;} for(int i=1;i<=maxL-5;i++)if(c[i]!=c[i-1])b[++cnt]=i; S=0;T=cnt+1; int m1=0; for(int i=1;i<=cnt;i++)if(b[i]&1){ m1++; insert(S,i,1); for(int j=1;j<=cnt;j++)if(!(b[j]&1)&&isprime(ab(b[i]-b[j])))insert(i,j,1); }else{ insert(i,T,1); } int ans=0; while(bfs()){ for(int i=S;i<=T;i++)cur[i]=first[i]; ans+=dinic(S,inf); } printf("%d",cnt-ans+(m1-ans)%2); return 0;}
View Code

 

转载于:https://www.cnblogs.com/onioncyc/p/7352408.html

你可能感兴趣的文章
《常微分方程教程》习题2-2,4:一个跟踪问题
查看>>
陶哲轩实分析例17.2.3
查看>>
兩個集合之間的全體部分函數可以形成一個集合
查看>>
Elementary Methods in Number Theory Exercise 1.2.17
查看>>
认识拨号计划 - Dialplan
查看>>
DataTable 的数据导出到 Excel
查看>>
委托由浅入深学习
查看>>
BZOJ 1012 [JSOI2008]最大数maxnumber
查看>>
权限管理[Linux]
查看>>
unity3d优化总结篇(二)
查看>>
自定义view,实现文本自动换行
查看>>
查看网页自动保存的密码
查看>>
【题解】【区间】【二分查找】【Leetcode】Insert Interval & Merge Intervals
查看>>
Hello,C++(7)函数模板和类模板
查看>>
网站使用https协议
查看>>
git 使用
查看>>
对软件工程的一点认识
查看>>
似然函数的概念【转载】
查看>>
认识IPv4分组
查看>>
第七篇、微信小程序-video组件
查看>>