codeforces 453 B Little Pony and Harmony Chest (状压dp)

本文介绍了一个利用状态压缩动态规划方法解决特定问题的案例:寻找一个与给定数组a对应的b数组,使得b数组中元素两两互质,并且b数组与a数组的元素差值之和最小。通过预处理素数状态和状态压缩,文章详细展示了如何通过递推公式找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:求一个b数组,b数组中的所有数互质,和a数组对应下标的数的差的绝对值最小。

考虑a数组中的所有数范围为[1,30]则,b数组取值只有可能为[1,59),因为如果取到59及其以后,肯定可以取1,59-30=30-1;而且1可以取多次,1与任何数互质。

然后首先需要把[2,59)之间的素数取出来,总共16个。然后状压,1代表那一位的素数因子是否存在。

d[i][s][0]表示递推到第i个位置的时候,为s状态下,第i个位置放的数。

d[i][s][1]表示递推到第i个位置的时候,为s状态下,最小值。

d[i][s][2]表示递推到第i个位置的时候,为s状态下,之前的状态。

st[i]表示当值为i的时候,素数因子的状态,初始化的时候全部算出。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mem(name,value) memset(name,value,sizeof(name))
#define FOR(i,n) for(int i=1;i<=n;i++)
using namespace std;
const int maxn=100+5;
const int inf=0x3f3f3f3f;
const int prime[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
const int maxp = 16;
int st[maxn],d[maxn][1<<maxp][3],a[maxn],n;
void init(){
    mem(st,0); mem(d,inf); mem(d[0],0);
    for(int i=1;i<=59;i++)
        for(int j=0;j<maxp;j++){
            if(prime[j]>i) break;
            if(i%prime[j]==0) st[i] |= (1<<j);  //如果j为i的因子,则该位取1
        }
}
void print(int cur,int pos){
    if(cur==0) return ;
    print(cur-1,d[cur][pos][2]);
    if(cur==n) printf("%d\n",d[cur][pos][0]);
    else printf("%d ",d[cur][pos][0]);
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    init();
    for(int i=1;i<=n;i++){
        for(int j=1;j<59;j++){
            int tmp = st[j];
            for(int k=0;k<(1<<16);k++){
                if(!(tmp&k)){
                    if(d[i-1][k][1]+abs(a[i]-j)<d[i][tmp|k][1]){
                        d[i][tmp|k][1] = d[i-1][k][1] + abs(a[i]-j);
                        d[i][tmp|k][0] = j;
                        d[i][tmp|k][2] = k;
                    }
                }
            }
        }
    }
    int ans=inf,pos=0;
    for(int i=0;i<(1<<16);i++){
        if(d[n][i][1]<ans){
            ans = d[n][i][1];
            pos = i;
        }
    }
    print(n,pos);
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值