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 }

原文地址:https://www.cnblogs.com/Leohh/p/8267677.html

时间: 2024-10-06 05:36:01

Codeforces 453B Little Pony and Harmony Chest:状压dp【记录转移路径】的相关文章

Codeforces 453B Little Pony and Harmony Chest 状压dp

题目链接:点击打开链接 b的数字最多只能达到59,因为选>=60 不如选1 所以状压一下前面出现过的素数即可,在59内的素数很少 然后暴力转移.. #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <string.h> const int Inf = (int)(1e9); const int S = 1 <<

Codeforces 453B Little Pony and Harmony Chest(状压)

题目链接:Codeforces 453B Little Pony and Harmony Chest 题目大意:给定一个序列a, 求一序列b,要求∑|ai?bi|最小.并且b中任意两数的最大公约束为1. 解题思路:因为b中不可能含有相同的因子,所以每个素数只能使用1次.又因为说ai最大为30,所以素数只需要考虑到57即可.因为即使对于30而言,59和1的代价是一样的. 所以有dp[i][j]表示的是到第i个数,使用过的素数j. #include <cstdio> #include <cs

codeforces 453 B Little Pony and Harmony Chest (状压dp)

题目大意: 需要你构造一个b数组.使得b数组中的所有元素互质. 而且使得b数组与a数组中的每个对应下标元素的差值和最小. 思路分析: 考虑到 a中所有元素都是 0 - 30. 所以b中的元素也只可能在 0 - 59. 因为如果b 选择60的话,结果和1是一样的,而且b序列中 1 可以重复出现很多次. 因为gcd (1,x) = 1.. 所以们首先把2 - 59中的所有素数处理出来,只有17个. 然后状压这些素数因子. dp[i] [s] [0] 表示 递推到第 i 个位置 达到素数因子为s的状态

Codeforces Round #259 (Div. 2) D. Little Pony and Harmony Chest 状压DP

D. Little Pony and Harmony Chest Princess Twilight went to Celestia and Luna's old castle to research the chest from the Elements of Harmony. A sequence of positive integers bi is harmony if and only if for every two elements of the sequence their gr

Codeforces 454D - Little Pony and Harmony Chest

454D - Little Pony and Harmony Chest 思路: 状压dp,由于1的时候肯定满足题意,而ai最大是30,所以只要大于等于59都可以用1替换,所以答案在1到59之间 然后筛出1到58之间的质数,只有16个,把1到58的数的状态由这16个质数表示,如果整除这个质数则二进制中这一位为1,否则则为0 状态:dp[i][j]表示到第i个数为止选取的数的状态为j的最小差和 初始状态:dp[0][0]=0 状态转移: dp[i+1][j|sta[k]]=min(dp[i+1][

Codeforces Gym 100610 Problem K. Kitchen Robot 状压DP

Problem K. Kitchen Robot Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100610 Description Robots are becoming more and more popular. They are used nowadays not only in manufacturing plants, but also at home. One programmer wit

Codeforces 31E TV Game 中途相遇法 状压dp

题目链接:点击打开链接 题意: 给定2*n长的数字. 把这个数字拆成2个长度为n的数字,且相对位置不变.使得拆后得到的2个数字的和最大. 输出一个方案. 显然是中途相遇法,先计算左半段,再计算右半段 分别状压左半段和右半段,注意左半段状压后要在末尾补上0. 代码估计哪里有小越界==,数组开大了一点才过..具体就不查了. #include<iostream> #include<stdio.h> #include<string.h> #include<string&g

BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )

状压dp, 然后转移都是一样的, 矩阵乘法+快速幂就行啦. O(logN*2^(3m)) --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define b(x) (1 <&l

Codeforces Round #259 (Div. 2)-D. Little Pony and Harmony Chest

题目范围给的很小,所以有状压的方向. 我们是构造出一个数列,且数列中每两个数的最大公约数为1; 给的A[I]<=30,这是一个突破点. 可以发现B[I]中的数不会很大,要不然就不满足,所以B[I]<=60左右.构造DP方程DP[I][J]=MIN(DP[I][J],DP[I][J^C[K]]+abs(a[i]-k)); K为我们假设把这个数填进数组中的数.同时开相同空间记录位置,方便输出结果.. #include<iostream> #include<stdio.h>