【cf453B】Little Pony and Harmony Chest

D. Little Pony and Harmony Chest
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Princess Twilight went to Celestia and Luna's old castle to research the chest from the Elements of Harmony.

A sequence of positive integers bi is harmony if and only if for every two elements of the sequence their greatest common divisor equals 1. According to an ancient book, the key of the chest is a harmony sequence bi which minimizes the following expression:

You are given sequence ai, help Princess Twilight to find the key.

Input

The first line contains an integer n (1 ≤ n ≤ 100) — the number of elements of the sequences a and b. The next line contains n integersa1, a2, ..., an (1 ≤ ai ≤ 30).

Output

Output the key — sequence bi that minimizes the sum described above. If there are multiple optimal sequences, you can output any of them.

Sample test(s)
input
5
1 1 1 1 1
output
1 1 1 1 1 
input
5
1 6 4 2 8
output
1 5 3 1 8 
题目的大意是,有n个数,构造n个数对应使得最小,并且构造出的这n个数要满足两两互质。

首先看一下这个数据范围,1<=n<=100,1<=ai<=30。这种大小的数据量容易想到是用状态压缩。

关系到互质问题,首先处理出数据范围内的质数,1~59内一共有17个质数,去掉59(取59和取1等价),一共有16个质数,然后需要确定状态,为了满足两两互质,这n个数两两之间的最大公约数都为1,每种质数至多出现一次,因此将状态按位表示,总共就有1<<16=65536种状态。

枚举状态state和当前位的数字k(对应状态为m),状态转移方程为dp[i][state|m]=dp[i-1][state]+abs(k-num[i])   因为满足state|m状态的k可能会有多个,我们只取最优的那个更新,所以只有当state|m未更新或者当这个状态的值可以变得更小才进行更新。最小值为min(dp[n-1][state])。注意一下边界初始化。

答案要求打印出一个解,那么只需要在dp的每个阶段的状态下保存数字k即可,首先找到最终的最优状态,再一步一步回推,把数字保存在数组再打印。


代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
using namespace std;

#define N (1<<16)+10
#define INF 0x3f3f3f3f
int a[105];//原数列
bool vis[105];
int dp[105][N][2];//dp[][][0]保存当前最小值 dp[][][1]保存当前阶段最小值对应的数字
int ans[105];//保存答案
int prime[30];//保存质数
int con[60];//保存1~60以内的数对应状态
int cnt;

int deal(int a){//返回对应状态
    int ret;
    ret=0;
    for(int i=0;i<cnt;i++){
        if(a%prime[i]==0) ret+=1<<i;
    }
    return ret;
}


void init(){//预处理
     cnt=0;
     memset(vis,0,sizeof(vis));
     prime[0]=2;
     for(int i=2;i<59;i++){
         for(int j=i*2;j<59;j+=i) vis[j]=1;
            if(!vis[i]) prime[cnt++]=i;
     }
     for(int i=1;i<59;i++){
        con[i]=deal(i);
     }
}


int main()
{
    int n,minn,tmp,state;
    init();
    cin>>n;
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
  //  if(n==1){
  //      printf("%d\n",a[0]);
  //      return 0;
  //  }
    memset(dp,-1,sizeof(dp));//初始化所有的值
    for(int i=1;i<59;i++){//边界初始化
        tmp=con[i];
        if(dp[0][tmp][0]>abs(a[0]-i)||dp[0][tmp][0]==-1){
        dp[0][tmp][0]=abs(a[0]-i);
        dp[0][tmp][1]=i;
        }
    }
    for(int i = 1; i < n; i++){//
        for(int k = 0; k < N; k++){//枚举所有状态
            if(dp[i - 1][k][0] == -1) continue;//如果枚举的状态不存在则跳过
           for(int j = 1; j < 59; j++){
              tmp = con[j];
              if(!(tmp&k)){//如果当前状态合法
                    if(dp[i][k|tmp][0]==-1||dp[i][k|tmp][0]>dp[i-1][k][0]+abs(a[i]-j)){//满足更新条件
                        dp[i][k|tmp][0]=dp[i-1][k][0]+abs(a[i]-j);
                        dp[i][k|tmp][1]=j;
                    }
              }
           }
        }
    }
    minn=INF;
    for(int i=0;i<N;i++){
        if(dp[n-1][i][0]!=-1&&minn>dp[n-1][i][0]){//找到最优解
            minn=dp[n-1][i][0];
            state=i;
        }
    }
    for(int i=n-1;i>=0;i--){//回退保存数字
        ans[i]=dp[i][state][1];
        tmp=con[ans[i]];
        state^=tmp;
    }
    for(int i=0;i<n;i++){
        printf("%d ",ans[i]);
    }
    printf("\n");
    return 0;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值