直接状态压缩,记录使用了哪些素数,达到的最小的改动和。因为给定的ai<=30,那么改动最大不超过58,30改成59和改成1得到的结果是一样的。
每次尝试将当前数字改成1到58,最后取最小值即可。代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <iomanip>
using namespace std;
#define ff(i, n) for(int i=0;i<(n);i++)
#define fff(i, n, m) for(int i=(n);i<=(m);i++)
#define dff(i, n, m) for(int i=(n);i>=(m);i--)
#define bit(n) (1LL<<(n))
typedef long long LL;
typedef unsigned long long ULL;
const LL inf=1e15;
void work();
int main()
{
#ifdef ACM
freopen("in.txt", "r", stdin);
#endif // ACM
work();
}
/***************************************************/
int h[60];
int pri[18];
int tot;
int dp[105][bit(16)];
int ss[105][bit(16)];
void calPrime()
{
fff(i, 2, 58) if(h[i] == 0)
{
pri[tot] = i;
h[i] ^= bit(tot);
for(int j=i+i;j<=58;j+=i)
h[j] ^= bit(tot);
tot++;
}
}
void work()
{
calPrime();
int n;
while(scanf("%d", &n) == 1)
{
memset(dp, 0x1f, sizeof(dp));
dp[0][0] = 0;
ff(i, n)
{
int cur;
scanf("%d", &cur);
ff(st, bit(tot)) if(dp[i][st] < 3000)
{
fff(j, 1, 58) if((st & h[j]) == 0 && dp[i+1][st ^ h[j]] > dp[i][st] + abs(cur - j))
{
dp[i+1][st ^ h[j]] = dp[i][st] + abs(cur - j);
ss[i+1][st ^ h[j]] = j;
}
}
}
int mi = *min_element(dp[n], dp[n]+bit(tot));
vector<int> ans;
ff(st, bit(tot)) if(mi == dp[n][st])
{
int state = st;
dff(i, n, 1)
{
ans.push_back(ss[i][state]);
state = state ^ h[ss[i][state]];
}
break;
}
if(n) printf("%d", ans[n-1]);
dff(i, n-2, 0) printf(" %d", ans[i]);
puts("");
}
}