CF Round 259 DD. Little Pony and Harmony Chest 状态压缩DP
题目链接:http://codeforces.com/contest/454/problem/D
题目大意:给你n个数字a1~an,要你找n个数字b1~bn满足Σ|ai - bi|最小,并且b1~bn这n个数互质,输出这组数。1 ≤ ai ≤ 30 1 ≤ n ≤ 100
解题思路:n个数互质即说明他们之间的质因子不相同,而由于ai <= 30,所以可以将60以内的素数打表然后进行状态压缩,以每一位的值表示对应素数是否是一个数的质因子。
然后就是针对每个ai,决策----60以内选一个满足条件的数。设dp[i][j]:=前i个数使用的素数为j所示时所求和的最小值,那么转移方程:
dp[i][ij] = min(dp[i][j], dp[i - 1][j ^ (k的质因子状态)] + abs(a[i] - k)) 1 <= k <= 60, k & j == 0
大体思路:
前16个素数(ai<= 30,所以59和1实际上是等价的)打表
对1~60的每个数计算对应状态,即 10110表示(从低位到高位)第1个素数不是其质因子,第二个,第三个素数是,第四个不是,第五个是。
初始化dp[0][i] = 0,循环求dp[i][j]并且记录路经
沿着路径查找压栈,最后输出。
代码:
1 const int maxn = 1e2 + 5, maxm = (1 << 16) + 5; 2 int n, a[maxn]; 3 int prifac[65], dp[maxn][maxm]; 4 struct node{ 5 int i, j, k; 6 }pre[maxn][maxm]; 7 8 void solve(){ 9 int primes[20] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}; 10 int pribit[20]; 11 for(int i = 0; i < 16; i++) pribit[i] = 1 << i; 12 for(int i = 1; i <= 60; i++) { 13 for(int j = 0; j < 16; j++){ 14 if(i % primes[j] == 0) prifac[i] = prifac[i] | pribit[j]; 15 } 16 } 17 18 memset(dp, 0x3f, sizeof(dp)); 19 memset(pre, 0, sizeof(pre)); 20 for(int i = 0; i < maxm; i++) dp[0][i] = 0; 21 for(int i = 1; i <= n; i++){ 22 for(int j = 0; j < (1 << 16); j++){ 23 for(int kk = 1; kk <= 60; kk++){ 24 int k = prifac[kk]; 25 if((k & j) == 0 && dp[i - 1][j ^ k] + abs(kk - a[i]) < dp[i][j]){ 26 dp[i][j] = dp[i - 1][j ^ k] + abs(kk - a[i]); 27 pre[i][j].i = i - 1; pre[i][j].j = j ^ k; pre[i][j].k = kk; 28 } 29 } 30 } 31 } 32 int mi = n, mj = 0; 33 int tmans = inf; 34 for(int i = 0; i < (1 << 16); i++){ 35 if(dp[n][i] < tmans) { 36 tmans = dp[n][i]; 37 mj = i; 38 } 39 } 40 stack<int> s; 41 while(mi > 0){ 42 s.push(pre[mi][mj].k); 43 int tmi = pre[mi][mj].i, tmj = pre[mi][mj].j; 44 mi = tmi; mj = tmj; 45 } 46 while(!s.empty()){ 47 printf("%d", s.top()); 48 s.pop(); 49 if(s.empty()) printf("\n"); 50 else printf(" "); 51 } 52 } 53 int main(){ 54 scanf("%d", &n); 55 for(int i = 1; i <= n; i++) scanf("%d", &a[i]); 56 solve(); 57 return 0; 58 }
题目: