第九届中国大学生程序设计竞赛(秦皇岛)-(CCPC2023-Qinhuangdao F - 质数之谜(DP)

题意

给定一个序列,修改最少数量的元素使得任意i属于[1,n-1],q[i]+q[i+1]都为质数,输出最小修改次数

思路


首先手玩的过程中可以发现,
    如果因为前面一个数字和自己相加不是质数然后我把自己变成了奇数,那么如果我后面一个数字是偶数
    可以发现自己肯定能找到另一个奇数使得和前面相加互质并且和后面相加也互质
    举个例子:假设为2 8 10,我此时是8,我发现2+8不是质数把自己改为5,但是5+10不是质数,但是无论如何我都可以找到一个数字(因为这个数字没有范围限制)x,使得x+2和x+10都是质数,这个样例中取x=3即可
由于前面的修改会影响后面的修改,所以考虑用线性DP来写
然后由发现的结论可以知道,假设前面的一个数字已经被修改过并且改为奇数,那么如果此时位置的数为偶数,那么前面一个数字一定能再找到一个奇数(还是相当于只修改一次)使得这个偶数和奇数互质
但是由于1(奇数)+1(奇数)奇偶性相同但是也满足结论,所以变1这个情况要特判

那么dp有4个状态,0:不变 1:变偶数 2:变奇数(非1) 3:变1

只要考虑每个状态可以由什么状态转移过来就可以解决这个问题

f[i-1][0]:如果原数组前后两个数互质时候(!st[q[i-1]+q[i]) 
f[i-1][1]:如果q[i]为奇数,并且前一个变成了偶数也就是f[i-1][1]
f[i-1][2]:如果q[i]为偶数,并且前一个变成了奇数也就是f[i-1][2]
f[i-1][3]:如果q[i]+1为质数
----->可以转移给f[i][0]状态

f[i-1][0]:q[i-1]为奇数的时候
f[i-1][1]:如论如何都不能转移
f[i-1][2]:一定可以转移
f[i-1][3]:一定可以转移
----->可以转移给f[i][1]状态
        
f[i-1][0]:q[i-1]为偶数的时候
f[i-1][1]:一定可以转移
f[i-1][2]:如论如何都不能转移
f[i-1][3]:如论如何都不能转移
----->可以转移给f[i][2]状态
        
f[i-1][0]:q[i-1]+1为奇数的时候
f[i-1][1]:一定可以转移
f[i-1][2]:如论如何都不能转移
f[i-1][3]:q[i-1]不等于1的时候(因为等于就不是变成1的情况了)可以转移
----->可以转移给f[i][3]状态
*/

代码实现

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
const int N=1e6+10,INF=4e18;
int n,m;
int q[N];
int f[N][5];
int prime[N],cnt;
bool st[N];
//0不变 1变偶 2变奇 3变1
void init()
{
    for(int i=2;i<=200000;i++)
    {
        if(!st[i])prime[cnt++]=i;
        for(int j=0;prime[j]*i<=200000;j++)
        {
            st[prime[j]*i]=true;
            if(i%prime[j]==0)break;
        }
    }
    return;
}
void solve()
{
    cin>>n;
    _rep(i,1,n)cin>>q[i];
    memset(f,0x3f,sizeof(f));
    f[1][0]=0;
    f[1][1]=f[1][2]=f[1][3]=1;
    _rep(i,2,n)
    {
        //0
        if(!st[q[i]+q[i-1]])f[i][0]=min(f[i][0],f[i-1][0]);
        if(q[i]%2)f[i][0]=min(f[i][0],f[i-1][1]);
        else f[i][0]=min(f[i][0],f[i-1][2]);
        if(!st[q[i]+1])f[i][0]=min(f[i][0],f[i-1][3]);
        
        //1,2
        if(q[i-1]%2)f[i][1]=min(f[i][1],f[i-1][0]+1);
        else f[i][2]=min(f[i][2],f[i-1][0]+1);
        f[i][1]=min(f[i][1],f[i-1][2]+1);
        f[i][1]=min(f[i][1],f[i-1][3]+1);
        f[i][2]=min(f[i][2],f[i-1][1]+1);
        
        //3
        if(!st[q[i-1]+1]&&q[i]!=1)f[i][3]=min(f[i][3],f[i-1][0]+1);
        f[i][3]=min(f[i][3],f[i-1][1]+1);
        if(q[i-1]!=1)f[i][3]=min(f[i][3],f[i-1][3]+1);
    }
    int res=INF;
//测试
//    for(int i=1;i<=n;i++)
//    {
//        int now=INF;
//        for(int j=0;j<=3;j++)
//        {
//            if(f[i][j]!=0x3f3f3f3f3f3f3f3f)
//            cout<<f[i][j]<<" ";
//            else cout<<"- ";
//        }
//        cout<<endl;
//    }
    _rep(i,0,3)res=min(res,f[n][i]);
    cout<<res<<"\n";
    return ;
}
signed main()
{
    IOS;
    int T=1;
    init();
//    cin>>T;
    while(T--)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值