1. 程式人生 > >Codeforces 453B Little Pony and Harmony Chest:狀壓dp【記錄轉移路徑】

Codeforces 453B Little Pony and Harmony Chest:狀壓dp【記錄轉移路徑】

輸出 兩個 cal bsp tac 可能 pan 才有 line

題目鏈接: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【記錄轉移路徑】