题解:
因为 1 <= ai <= 30 所以 1 <= bi <= 58, 因为 59 和 1 等效, 所以不需要59。
[1, 58]只有16个质数,对于这16个质数去状压。
对于1->58的数,我们计算出每个数对于质数来说的状态,然后转移。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const int _inf = 0xc0c0c0c0; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL _INF = 0xc0c0c0c0c0c0c0c0; const LL mod = (int)1e9+7; const int N = 2e5 + 100; int prim[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; int state[60]; int n; int a[105]; int dp[105][1<<17]; int pre[105][1<<17]; int use[105][1<<17]; void P(int x, int v){ if(!x) return; P(x-1, pre[x][v]); printf("%d ", use[x][v]); } int Ac(){ scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); int tot = 1<<16; for(int i = 2; i <= 58; ++i) for(int j = 0; j < 16; ++j) if(i%prim[j] == 0) state[i] |= 1 << j; memset(dp, inf, sizeof dp); dp[0][0] = 0; for(int i = 0; i < n; ++i){ for(int j = 0; j < tot; ++j){ if(dp[i][j] == inf) continue; for(int k = 1; k <= 58; ++k){ if(j & state[k]) continue; int x = j | state[k]; if(dp[i+1][x] > dp[i][j] + abs(a[i+1] - k)){ dp[i+1][x] = dp[i][j] + abs(a[i+1] - k); pre[i+1][x] = j; use[i+1][x] = k; } } } } int id = 0; for(int j = 1; j < tot; ++j){ if(dp[n][id] > dp[n][j]){ id = j; } } P(n, id); return 0; } int main(){ Ac(); return 0; }