这个题和SW学长过去拉的一个 POJ2836 Rectangular Covering 让我认识到,什么东西都可以压缩成二进制状态,这个题也是,给你n个数,问你将每个数变化,最终得到的n个数最大公约数为1,并且使得所有数字变化前的数与变化后的数的差的绝对值之和最小,此题数字最大为30,所以我们可以知道变化的数字最大也只能到59(当数字为30时,30与1的差和59的差相同)之后将1到60每个数字中包含的素数设置成状态量,并打表得到所有数字的状态,之后便可得到关系式对于每个状态j,如果我要将数字k添加到此状态中,当且仅当k包含的素数与状态j没有相同的素数,即(j&val【k】)==0,这时dp【i】【j】=min(dp【i】【j】,dp【i-1】【j^val【k】】+fabs(k-a【i】),递推便可得到答案,但是由于此题需要输出变换后的n个数,所以还需要设置一个ans数组保存每个状态的转移情况。。。最终直接从ans数组中反向寻找递推路径,用栈保存,之后输出即可。。。具体程序如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
int pri[20],cnt,vis[65],a[105],val[60],dp[105][1<<17],ans[105][1<<17];
void init()
{
cnt=0;
memset(vis,0,sizeof(vis));
for(int i=2;i<=60;i++)
{
if(vis[i]==0)
{
pri[cnt]=i;
cnt++;
for(int j=i*i;j<=60;j+=i)
vis[j]=1;
}
}
memset(val,0,sizeof(val));
for(int i=1;i<60;i++)
{
for(int j=0;j<cnt;j++)
{
if(i%pri[j]==0)
val[i]=val[i]|(1<<j);
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
init();
int n;
while(cin>>n)
{
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=0;i<=n;i++)
{
for(int j=0;j<(1<<cnt);j++)
dp[i][j]=inf;
}
for(int i=0;i<(1<<cnt);i++)
dp[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<(1<<cnt);j++)
{
for(int k=1;k<60;k++)
{
if((j&val[k])==0)
{
int res=dp[i-1][j^val[k]]+fabs(k-a[i]);
if(dp[i][j]>res)
{
dp[i][j]=res;
ans[i][j]=k;
}
}
}
}
}
int res=inf,state;
for(int i=0;i<(1<<cnt);i++)
{
if(res>dp[n][i])
{
res=dp[n][i];
state=i;
}
}
stack<int> q;
for(int i=n;i>0;i--)
{
q.push(ans[i][state]);
int k=ans[i][state];
state=state^val[k];
}
while(!q.empty())
{
cout<<q.top()<<" ";
q.pop();
}
cout<<endl;
}
return 0;
}