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 }

 

posted @ 2018-01-11 11:57  Leohh  阅读(205)  评论(0编辑  收藏  举报