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];
}
}