https://codeforces.com/problemset/problem/453/B
题目
给一串数字A,要求你找出一串数字B,满足
- 这串数字两两互质
- $sum leftlvert A[i]-B[i] ight vert$最小
$A[i]leqslant 30$,$nleqslant 100$
题解
因为1可以随便用,所以B[i]不会超过2倍A[i],否则不如直接用1
于是B[i]的取值范围是[1,59]
如果直接记录不能用的数字,直接开数组存不下((1<<59)*100)
如果存不能用的质因数就可以存下,打表发现质因数只有17个,所以可以记录下所有的状态((1<<17)*100)
那么可以写出转移方程
设dp[i][k]为从第i个数开始,已经用了的质因数有k(二进制压位)
$dp[i][k]=min (dp[i+1][k^fac[t]]+abs(t-A[i]))$
直接枚举状态和转移需要$100 imes 2^{17} imes 60$
其实这个$2^{17}$可以优化一下,t有m位是1的时候,可以省到$frac{1}{2^m}$
虽然还可以滚动优化,并且把记录改为char……
AC代码
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cassert> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; #define MAXN 107 #define MAXP 17 int ps[MAXP]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59}; unsigned fa[]={0,0,1,2,1,4,3,8,1,2,5,16,3,32,9,6,1,64,3,128,5,10,17,256,3,4,33,2,9,512,7,1024,1,18,65,12,3,2048,129,34,5,4096,11,8192,17,6,257,16384,3,8,5,66,33,32768,3,20,9,130,513,65536,7}; int n; int arr[MAXN]; int dp[MAXN][1<<MAXP]; int go[MAXN][1<<MAXP]; int main() { scanf("%d", &n); REP(i,0,n) scanf("%d", &arr[i]); memset(dp,0x3f,sizeof(dp[0])*n); memset(dp[n],0,sizeof(dp[n])); PERE(i,n-1,0) { REPE(t,1,60) { unsigned qf=((1<<MAXP)-1)^fa[t]; for(unsigned k=qf; k>0; k=(k-1)&qf) { int nd=dp[i+1][k|fa[t]]+abs(t-arr[i]); if(dp[i][k]>nd) { dp[i][k]=nd, go[i][k]=t; } } int nd=dp[i+1][fa[t]]+abs(t-arr[i]); if(dp[i][0]>nd) { dp[i][0]=nd, go[i][0]=t; } } } int now=0; REPE(i,0,n-1) { int nt=go[i][now]; printf("%d%c", nt, " "[i==n-1]); now|=fa[nt]; } }