题目如下:
分析:因为ai的范围时1-30,而数组全部为1时肯定满足条件的,所以只需要找1-60内的值就够了。而另一个要求是gcd为一,也就是没有除了1以为的共同因子,那么可以先把每个数的因子求出来,已位的形式表达,两个数要是gcd为1的话,则需要两个位表达式与一下为0,如4的因子为2,我们标记2所在的那一位为1,当出现8时,8的因子也为2,而看到2所在的那位已经是1了,所以8可能不可能是合法的值了。转移方程为dp[i][j|p(k)] = min(dp[i][j|p(k)],dp[i-1][j]+abs(a[i]-k)),意思是当第i-1个数结束后状态为j的时候,在第i个位置的地方选择了数值k,那么当前的状态就是dp[i][j|p(k)], p(k)即k用位表达的值,当然进行转移的前提是p(k)&j==0,不然就不满足gcd为1的条件了,其他变量在注视中写的挺清楚了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
using namespace std;
const int maxn=62;
int top;
int primes[maxn];
int prime[maxn];
int N=(1<<16);
void make()
{
top=0;
int i, j;
for(i = 2; i < maxn; i++)
{
if(prime[i]) continue;
primes[top++] = i;
for(j = 2; i * j < maxn; j++)
{
prime[i * j] = 1;
}
}
}
int dp[105][(1<<17)+10];
int pre[105][(1<<17)+10];
int anss[105][(1<<17)+10];
int a[105];
int bit[65];
int as[105];
int gt(int x)
{
int ans=0;
for(int i=0; i<top; i++)
{
if(x%primes[i]==0)
{
ans+=(1<<i);
while(x%primes[i]==0)
{
x/=primes[i];
}
}
}
return ans;
}
void init()
{
for(int i=1; i<=60; i++)
{
bit[i]=gt(i);
}
}
int inf=30000;
int main()
{
int n;
while(~scanf("%d",&n))
{
make();
init();
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=0; i<105; i++)
for(int j=0; j<N; j++)
{
dp[i][j]=inf;
pre[i][j]=-1;
anss[i][j]=0;
}
dp[0][0]=0;
for(int i=1; i<=n; i++)
{
for(int j=0; j<N; j++)
{
for(int k=1; k<=60; k++)
{
if(dp[i-1][j]!=inf&&(bit[k]&j)==0)
{
if(dp[i][bit[k]|j]>(dp[i-1][j]+abs(a[i]-k)))
{
dp[i][bit[k]|j]=(dp[i-1][j]+abs(a[i]-k));
anss[i][bit[k]|j]=k;
pre[i][bit[k]|j]=j;
}
}
}
}
}
int ans=inf;
int tar=0;
for(int i=0; i<N; i++)
{
if(ans>dp[n][i])
{
ans=dp[n][i];
tar=i;
}
}
stack<int> s;
int sta=n;
s.push(anss[sta][tar]);
while(pre[sta][tar]!=-1)
{
tar=pre[sta][tar];
sta--;
s.push(anss[sta][tar]);
}
s.pop();
while(!s.empty())
{
printf("%d ",s.top());
s.pop();
}
printf("\n");
}
}