第九届中国大学生程序设计竞赛(秦皇岛)-(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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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"<=(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 pairPII;
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<>T;
    while(T--)
        solve();
    return 0;
}

你可能感兴趣的:(算法,c++,动态规划)