Codeforces 453B Little Pony and Harmony Chest:状压dp【记录转移路径】
题目链接:http://codeforces.com/problemset/problem/453/B
题意:
给你一个长度为n的数列a,让你构造一个长度为n的数列b。
在保证b中任意两数gcd都为1的情况下,使得 ∑|a[i]-b[i]|最小。
让你输出构造的数列b。
(1<=n<=100, 1<=a[i]<=30)
题解:
因为1<=a[i]<=30,所以有1<=b[i]<=60,此时才有可能最优。
因为b中任意两数gcd为1,所以对于一个质因子p[i]只会在一个b[i]中用到。
所以先处理出1到60这些数所要用到的质因子,状压存在数组f[i]中,第i位为1表示要用到质因子p[i]。
另外,这题中59这个质因子是用不到的。
因为它能构成的60以内的数只有59,然而对于最大的a[i]=30来说,b[i]选59和选1是等效的。
这样就只剩16个质因子了。否则用17个会被卡时间和空间。
然后开始状压dp。
表示状态:
dp[i][state] = min value
表示该构造b[i]了,质因子的状态为state,此时原式的最小值。
如何转移:
dp[i+1][state|(1<<j)] = min dp[i][state] + |a[i]-j|
枚举当前b[i]选了j,然后转移。
边界条件:
dp[0][0] = 0
ohters = INF
改构造b[0]了,此时一个质因子还没用过,原式初始为0。
找出答案:
枚举质因子状态state,显然最小的dp[n][state]为答案。
然而现在只是知道了原式能达到的最小值,并不知道构造出的b数列。
所以在转移的时候要记录下转移路径。
新开两个数组:
sel[i][state]:表示从上一步转移到这一步时,b[i-1]选了哪个数字
sta[i][state]:若状态(i,state)是由(i-1,pre)转移而来的,则sta[i][state]为pre的值。
所以每次转移的时候将路径和所选的数记录下来:
if(dp[i][state]+d < dp[i+1][nex])
{
dp[i+1][nex]=dp[i][state]+d;
sel[i+1][nex]=j;
sta[i+1][nex]=state;
}
然后从最终答案的那一步,一直往前一个状态跳,就能找出构造的b数列了。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <stack> 5 #define MAX_N 105 6 #define MAX_P 20 7 #define MAX_D 65 8 #define MAX_S ((1<<16)+50) 9 #define INF 1000000000 10 11 using namespace std; 12 13 const int p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; 14 15 int n; 16 int a[MAX_N]; 17 int dp[MAX_N][MAX_S]; 18 int sel[MAX_N][MAX_S]; 19 int sta[MAX_N][MAX_S]; 20 int f[MAX_D]; 21 22 inline int abs(int x) 23 { 24 return x>0 ? x : -x; 25 } 26 27 int get_f(int x) 28 { 29 int state=0; 30 for(int i=0;i<16;i++) 31 { 32 while(x%p[i]==0) 33 { 34 x/=p[i]; 35 state|=(1<<i); 36 } 37 } 38 return state; 39 } 40 41 void cal_f() 42 { 43 for(int i=1;i<=60;i++) 44 { 45 f[i]=get_f(i); 46 } 47 } 48 49 void cal_dp() 50 { 51 memset(dp,0x3f,sizeof(dp)); 52 dp[0][0]=0; 53 for(int i=0;i<n;i++) 54 { 55 for(int state=0;state<(1<<16);state++) 56 { 57 if(dp[i][state]<INF) 58 { 59 for(int j=1;j<=60;j++) 60 { 61 if(!(state&f[j])) 62 { 63 int nex=(state|f[j]); 64 int d=abs(a[i]-j); 65 if(dp[i][state]+d<dp[i+1][nex]) 66 { 67 dp[i+1][nex]=dp[i][state]+d; 68 sel[i+1][nex]=j; 69 sta[i+1][nex]=state; 70 } 71 } 72 } 73 } 74 } 75 } 76 int ans=INF; 77 int now; 78 for(int state=0;state<(1<<16);state++) 79 { 80 if(dp[n][state]<ans) 81 { 82 ans=dp[n][state]; 83 now=state; 84 } 85 } 86 stack<int> stk; 87 for(int i=n;i>=1;i--) 88 { 89 stk.push(sel[i][now]); 90 now=sta[i][now]; 91 } 92 while(!stk.empty()) 93 { 94 cout<<stk.top()<<" "; 95 stk.pop(); 96 } 97 cout<<endl; 98 } 99 100 int main() 101 { 102 cin>>n; 103 for(int i=0;i<n;i++) cin>>a[i]; 104 cal_f(); 105 cal_dp(); 106 }