codeforces 453B B. Little Pony and Harmony Chest (状压dp+数论)

题目链接:

http://codeforces.com/contest/453/problem/B

题意:

给出一个序列a,求取一个序列b,b序列的数两两互质,问能够导致|aibi|最小的方案

思路:

http://blog.csdn.net/qq_24451605/article/details/48878237 orz

  • 定义状态dp[i][j]表示前i个数达到j状态的最小的结果,j状态表示已经被用过的质数。
  • 因为当一个a的数据范围不超过30,所以如果某个数超过60,那么选择1一定比它更优,所以我们能够用到的数的质因子也一定不会超过60,也才17个,所以我们只需要每次枚举数,然后通过之前的状态进行转移即可。
  • 具体转移过程见代码。
  • 为了得到方案,我们记录了pre[i][j]代表前i个数达到状态j得到最大值的前一个状态,num[i][j]表示前i个数达到状态j得到最大值的最后选取的数字。 

state[k] 表示k的所有质因子,用了k,就不能用所有k的质因子了

转移: dp[i+1][x] = min(dp[i+1][x],dp[i][j]+abs(a[i+1]-k))  【 b[i+1]=k;  x=state[k] | j 】

为什么对质数进行状压嘞?首先不同的质数是可以选的,一个质数只能用一次,那么如何分辨非质数是否可选? 其实就是判断这个非质数A的质因子是否已经用过了,假如前面已经选了一个非质数B,那么如果A与B有公因子x,即使x不是质数,也一定可以分解成质数,这样只需判断质数是否用过就好了。

挺有收获的,状压状压!!!

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;

int n,a[105];
int dp[105][1<<16],pre[105][1<<16],num[105][1<<16],state[105];

int prime[20] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

void print(int x,int st){
    if(x == 1){
        printf("%d ",num[x][st]);
        return ;
    }
    
    print(x-1,pre[x][st]);
    printf("%d ",num[x][st]);
}

int main(){
    cin >> n;
    for(int i=1; i<=n; i++)
        cin >> a[i];
    memset(dp,INF,sizeof(dp));
    dp[0][0] = 0;

    for(int k=2; k<59; k++){
        for(int j=0; j<16; j++)
            if(k%prime[j] == 0)
                state[k] |= (1<<j);
    }
    int tot = 1<<16;
    for(int i=0; i<n; i++){
        for(int j=0; j<tot; j++){
            if(dp[i][j]==INF) continue;
            for(int k=1; k<59; k++){
                if(state[k]&j) continue;
                int x = state[k]|j;
                if(dp[i+1][x] > dp[i][j]+abs(a[i+1]-k)){
                    dp[i+1][x] = dp[i][j]+abs(a[i+1]-k);
                    pre[i+1][x] = j;
                    num[i+1][x] = k;
                }
            }
        }
    }
    int minn = INF,st;
    for(int j=0; j<tot; j++){
        if(minn > dp[n][j]){
            minn = dp[n][j];
            st = j;
        }
    }
    print(n,st);
    puts("");

    return 0;
}

 

posted @ 2017-07-09 23:53  _yxg123  阅读(258)  评论(0编辑  收藏  举报