Codeforces 453B Little Pony and Harmony Chest(状压)
题意:给一个a数组,求出一个数组b使得b数组和a数组的差值的绝对值最小,并且b数组里面的数字两两互质.
解析:
由于ai的范围<=30,所以>58的数字就可以被1取代,
考虑到b数组里面的数字两两互质,即质因子最多出现一次
所有质因子只有<=58的16个,状态压缩就比较明白了.
具体看代码注释:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define inf 2099999999
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep1(i,a,b) for(int i=a;i>=b;i--)
const int N=1e2+10;
const int all=1<<16;
int ar[N];
int vis[N][all+5];//表示状态是否到达过
int dp[N][all+5];//dp[i][j]表示第i个数字所到达的第j个状态
int s[N];//s[i]表示i这个数字所占有的质因子
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
/**
*/
void dfs(int d,int S)
{
if(d==0) return ;
int u=vis[d][S];
dfs(d-1,S^s[u]);//把S中对应的质因子去掉
cout<<u<<' ';
}
int main()
{
#ifdef LOCAL_DEFINE
freopen("D://rush.txt", "r", stdin);
#endif
ios::sync_with_stdio(false),cin.tie(0);
int n;
for(int i=1;i<60;i++)
{
int a=i;
for(int j=0;j<16;j++)
{
while(a%prime[j]==0)
{
a/=prime[j];
s[i]|=(1<<j);
}
}
}
cin>>n;
rep(i,1,n) cin>>ar[i];
memset(vis,-1,sizeof vis);
vis[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<all;j++)//在第i个数前的所有状态
{
if(vis[i-1][j]!=-1)//这个状态之前达到过
{
for(int k=1;k<60;k++)
{
if((j&s[k])==0)//要选的这个数和已经存在的状态没有相同的质因子
//这个括号让我调试了一下午...
{
int y = j|s[k];//添加新的质因子
if(vis[i][y]==-1||dp[i-1][j]+abs(k-ar[i])<dp[i][y])
{
vis[i][y]=k;
dp[i][y]=dp[i-1][j]+abs(k-ar[i]);
}
}
}
}
}
}
int ans=inf,id=0;
for(int i=0;i<all;i++)
{
if(dp[n][i]<ans&&vis[n][i]!=-1)
{
ans=dp[n][i];
id=i;
}
}
dfs(n,id);
}