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.
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 the key — sequence bi that minimizes the sum described above. If there are multiple optimal sequences, you can output any of them.
5 1 1 1 1 1
1 1 1 1 1
5 1 6 4 2 8
1 5 3 1 8
首先看一下这个数据范围,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;
}