zoukankan      html  css  js  c++  java
  • 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 992C(数学)
    Codeforces 990C (思维)
    Codeforces 989C (构造)
    POJ 1511 Invitation Cards(链式前向星,dij,反向建边)
    Codeforces 1335E2 Three Blocks Palindrome (hard version)(暴力)
    POJ 3273 Monthly Expense(二分)
    POJ 2566 Bound Found(尺取前缀和)
    POJ 1321 棋盘问题(dfs)
    HDU 1506 Largest Rectangle in a Histogram(单调栈)
    POJ 2823 Sliding Window(单调队列)
  • 原文地址:https://www.cnblogs.com/Leohh/p/8267677.html
Copyright © 2011-2022 走看看