LGTB与序列 状压dp

时间:2023-03-08 19:29:35

考试一看我就想到了状压dp。当时没有想到素数,以为每一位只有0~9这些数,就开始压了。后来发现是小于30,然后改到了15,发现数据一点不给面子,一个小点得数都没有,完美爆零。。

考虑到bi最多变成58,如果变成更大的数还不如变成1,而且58之内只有16个素数,所以就压这16个素数就行了。就是他们的素数因子。

f[i][j]表示到第i位质因数压进去状态为j时最短序列;

f[i][j|prime[k]]=min(f[i][j|prime[k]],f[i-1][j]+abs(a[i]-k));

注意排序一下这些数,找前16个就行,因为最多有16个素数,其他的就都是1了,不要忘了最后加上。

PS:我的代码打表打过去的。借用一下同学的。

#include
#include
#include
#include
#include
#include
using namespace std;
int n,ji=0,jishu,shu=0x7fffffff;
int a[150],b[150],prime[100],f[110][1<<16],zhi[70];
bool su[5050];
int pai(const int a,const int b)
{
  return a>b;
}
int xiao(int x,int y)
{
  if(x<=y)  return x;
  else  return y;
}
int lowbit(int x)
{
  return x&-x;
}
int zhao(int x)
{
  int shu=0;
  while(x>0)
  {
    if(x&1)
      return shu;
    x>>=1;
    shu++;
  }
  return shu;
}
int count(int x)
{
  int shu=0;
  while(x>0)
  {
    if(x&1)
      shu++;
    x>>=1;
  }
  return shu;
}
int main()
{
  //freopen("seq.in","r",stdin);
  //freopen("seq.out","w",stdout);
  memset(su,0,sizeof(su));
  memset(f,0x3f,sizeof(f));
  memset(prime,0,sizeof(prime));
  memset(zhi,0,sizeof(zhi));
  cin>>n;
  for(int i=1;i<=n;++i)
    cin>>a[i];
  f[0][0]=0;
  sort(a+1,a+n+1,pai);
  su[1]=1;
  for(int i=2;i<=10;++i)
    for(int j=2;j<=50;++j)
      su[i*j]=1;
  for(int i=2;i<=58;++i)
    if(su[i]==0)
      prime[i]=++ji;
  for(int i=2;i<=61;++i)
    for(int j=2;j<=i;++j)
    {
      if(i%j==0&&prime[j]!=0)
        zhi[i]|=(1<<(prime[j]-1));
    }
  for(int i=0;i
    for(int j=0;j<(1<<16);++j)
      for(int k=1;k<=60;++k)
      {
        if((j&zhi[k])==0)
          f[i+1][j|zhi[k]]=xiao(f[i+1][j|zhi[k]],f[i][j]+abs(a[i+1]-k));
      }
    for(int i=0;i<(1<<16);++i)
      if(f[xiao(n,16)][i]
        shu=f[xiao(n,16)][i];
  if(n<=16)
    cout<<shu;
  else
  {
    for(int i=17;i<=n;++i)
      shu+=abs(a[i]-1);
    cout<<shu;
  }
  //system("pause");
  return 0;
}