题解:
给出n个数a[n],然后要求一另外n个数b[n]满足 sum{|a[i]-b[i]|}最小。b[n]内的元素要满足任意两个都是互质的。
题解:
状态压缩,为什么?对于这样数据方位小的求最有解并且要某个状态要表示的东西很多那么普通dp绝对不行,那么久可以考虑状态压缩,dp[i][j]表示前i个数素数因子的选取状态为j时的最小差值和。
说实话想不到这样状压,看了大犇的才懂,这样技巧性比较大,同时又用到了数论的(数论渣渣啊!)。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<map> using namespace std; typedef long long lld; const int oo=0x3f3f3f3f; const lld OO=1e18; const int Mod=1000000007; const int maxn=100000+5; int dp[105][1<<17],path[105][1<<17];///dp[i][j]表示前i个数,素因子的选取状况为j时的最小值 int ans[105],a[105]; int prime[20]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 57}; int fac[66]; int n; void get_ans(int i,int st) { if(i==0) return ; int k=path[i][st]; get_ans(i-1,st^fac[k]); ans[i]=k; } void init_fac() { memset(fac,0,sizeof fac); for(int i=1;i<=60;i++) { int num=i; for(int j=0;j<17;j++) while(num%prime[j]==0) { num/=prime[j]; fac[i]|=(1<<j); } } } void Dp() { int all=(1<<17)-1; memset(dp,-1,sizeof dp); dp[0][0]=0; for(int i=0;i<n;i++) { for(int j=0;j<=all;j++)///枚举状态 { if(dp[i][j]==-1)continue; for(int k=1;k<=60;k++)///枚举下个数选什么 { if(j&fac[k])continue;///如果素数因子存在有相同的说明肯定会有大于1的最大公约数,删去! int st=j|fac[k]; if(dp[i+1][st]==-1||dp[i+1][st]>dp[i][j]+abs(k-a[i+1])) { dp[i+1][st]=dp[i][j]+abs(k-a[i+1]); path[i+1][st]=k;///存入选了什么数 } } } } int res=oo,st; for(int i=0;i<=all;i++) { if(dp[n][i]!=-1&&res>dp[n][i]) { res=dp[n][i]; st=i; } } get_ans(n,st); for(int i=1;i<n;i++) printf("%d ",ans[i]); printf("%d\n",ans[n]); } void Input() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); } int main() { init_fac(); Input(); Dp(); return 0; }