Codeforces 454D Little Pony and Harmony Chest【思维+状压Dp+记录路径】好题!

来源:互联网 发布:党规党纪面前知敬畏守 编辑:程序博客网 时间:2024/06/06 14:27

D. Little Pony and Harmony Chest
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

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 greatest common divisor equals 1. According to an ancient book, the key of the chest is a harmony sequence bi which minimizes the following expression:

You are given sequence ai, help Princess Twilight to find the key.

Input

The first line contains an integer n (1 ≤ n ≤ 100) — the number of elements of the sequences a and b. The next line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 30).

Output

Output the key — sequence bi that minimizes the sum described above. If there are multiple optimal sequences, you can output any of them.

Examples
input
51 1 1 1 1
output
1 1 1 1 1 
input
51 6 4 2 8
output
1 5 3 1 8 

题目大意:


给出长度为N的一个序列A,让我们找到一个等长度序列B,使得Σabs(Ai-Bi)最小,要求B数组中,任取两个数字的Gcd为1,如果存在多解,输出一个即可。


思路:

需要不断去想优化时间复杂度的一个题,蛮不错的。


①首先考虑,对于这样的一个数组B,其置为1的个数可以为无限个,因为任何数和1的gcd都一定是1.那么我们再根据Ai的数据范围(1~30)可以得知,我们对于Bi的取值范围,一定介于(1~59)之间,因为我们假设取了值60.那么是不如直接取1来的更优的(对于任何数来讲都是一定的)。


②我们既然界定了B数组的取值范围,那么接下来考虑这类问题的特性,既然提到了Gcd,那么我们也一定能够联想到素数。如果对于一个位子的取值为K的时候,我们知道,如果K%pi(pi表示某个素数)==0的话,我们之后的位子中,就不能再存在一个数字X,使得X%pi==0了。

所以我们对于当前特性,我们可以设定Dp【i】【j】表示我们dp过程进行到位子i,对于素数占用的情况为j的最优解。因为60以内的素数个数并不多(17个),所以我们接下来去状压dp即可。


③我们预处理出num【k】,表示我们数字k会占用的素数的二进制表示。那么对于状态转移方程不难写出(从当前状态转移到下一状态):

Dp【i+1】【j+num【k】】=min(Dp【i+1】【j+num【k】】,Dp【i】【j】+abs(k-a【i+1】));【需要保证(j&num[k])==0】;


过程维护一下即可,需要输出路径,我们再维护一个数组pre【i】【j】,表示转移到状态Dp【i】【j】的时候,最后拿取的数字。

然后逆序走一波就行了。


Ac代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>#include<vector>using namespace std;int n;int a[103];int dp[103][(1<<17)];int pre[103][(1<<17)];int su[17]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};int num[65];void init(){    int end=(1<<17);    for(int i=0; i<=n; i++)    {        for(int j=0; j<end; j++)        {            dp[i][j]=0x3f3f3f3f;        }    }    memset(num,0,sizeof(num));    for(int i=2; i<=60; i++)    {        for(int j=0; j<17; j++)        {            if((i%su[j])==0)num[i]+=(1<<j);        }    }}int main(){    while(~scanf("%d",&n))    {        int end=(1<<(17));        init();        for(int i=1; i<=n; i++)scanf("%d",&a[i]);        dp[0][0]=0;        for(int i=0; i<n; i++)        {            for(int j=0; j<end; j++)            {                if(dp[i][j]!=0x3f3f3f3f)                {                    if(dp[i][j]+abs(a[i+1]-1)<dp[i+1][j])                    {                        dp[i+1][j]=dp[i][j]+abs(a[i+1]-1);                        pre[i+1][j]=1;                    }                    for(int k=2; k<=60; k++)                    {                        if((num[k]&j)==0)                        {                            if(dp[i][j]+abs(k-a[i+1])<dp[i+1][j+num[k]])                            {                                dp[i+1][j+num[k]]=dp[i][j]+abs(k-a[i+1]);                                pre[i+1][j+num[k]]=k;                            }                        }                    }                }            }        }        int pos;        int output=0x3f3f3f3f;        for(int j=0; j<end; j++)        {            if(dp[n][j]<output)            {                pos=j;                output=dp[n][j];            }        }        int level=n;        vector<int>ans;        while(level>0)        {            ans.push_back(pre[level][pos]);            pos-=num[pre[level][pos]];            level--;        }        reverse(ans.begin(),ans.end());        for(int i=0;i<ans.size();i++)        {            printf("%d ",ans[i]);        }        printf("\n");    }}









阅读全文
0 0
原创粉丝点击