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:

 #include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#define MAX_N 105
#define MAX_P 20
#define MAX_D 65
#define MAX_S ((1<<16)+50)
#define INF 1000000000 using namespace std; const int p[]={,,,,,,,,,,,,,,,}; int n;
int a[MAX_N];
int dp[MAX_N][MAX_S];
int sel[MAX_N][MAX_S];
int sta[MAX_N][MAX_S];
int f[MAX_D]; inline int abs(int x)
{
return x> ? x : -x;
} int get_f(int x)
{
int state=;
for(int i=;i<;i++)
{
while(x%p[i]==)
{
x/=p[i];
state|=(<<i);
}
}
return state;
} void cal_f()
{
for(int i=;i<=;i++)
{
f[i]=get_f(i);
}
} void cal_dp()
{
memset(dp,0x3f,sizeof(dp));
dp[][]=;
for(int i=;i<n;i++)
{
for(int state=;state<(<<);state++)
{
if(dp[i][state]<INF)
{
for(int j=;j<=;j++)
{
if(!(state&f[j]))
{
int nex=(state|f[j]);
int d=abs(a[i]-j);
if(dp[i][state]+d<dp[i+][nex])
{
dp[i+][nex]=dp[i][state]+d;
sel[i+][nex]=j;
sta[i+][nex]=state;
}
}
}
}
}
}
int ans=INF;
int now;
for(int state=;state<(<<);state++)
{
if(dp[n][state]<ans)
{
ans=dp[n][state];
now=state;
}
}
stack<int> stk;
for(int i=n;i>=;i--)
{
stk.push(sel[i][now]);
now=sta[i][now];
}
while(!stk.empty())
{
cout<<stk.top()<<" ";
stk.pop();
}
cout<<endl;
} int main()
{
cin>>n;
for(int i=;i<n;i++) cin>>a[i];
cal_f();
cal_dp();
}
上一篇:Jvisualvm 添加插件


下一篇:AsyncTask 处理耗时操作&&显示进度条