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 }
Codeforces 453B Little Pony and Harmony Chest:狀壓dp【記錄轉移路徑】