Thursday, January 8, 2015

Codeforces 453B - Little Pony and Harmony Chest

Problem Statement:
453B - Little Pony and Harmony Chest

Solution:
Learnt new thing thanks to the official editorial :)

The low constraints for both n and ai suggests that we can do a complete search, and in fact we can use a bitmasking technique to perform this search more efficiently. Firstly, to minimize |aibi|, we would like to choose bi that can do better than just simply assigning 1 to bi. This means that bi should be between [1..2ai1], and thus 2*30-1 = 59 marks the upperbound for bi. Our strategy would be simply try each of these 59 numbers on each i, but simply trying all combinations would be too slow. So what we can do a dynamic programming approach: d[i][bm] be the minimum possible ik=1|akbk|, while bm indicates the bitmask, where if k-th bit is on, then we have assigned number k a bi. However, using such bitmask prove to be too big in space, since bm can go up to 259. We can do better, but just bitmasking the prime factors that have been used (i.e. if bit k is set, then some bi is divisible by k-th prime)! There are only 17 primes in [1..59], so we reduced the bitmask states to 217 :D



Next, pre-calculate factor[k], which is a bitmask where i-th bit is on if number k is divisible by i-th prime. To fill up d[i][bm], we can make use of values in d[i-1], as follows:
Suppose we want to assign bi with j. That means we want to find all bitmask bm such that factor[j] AND bm does not intersect at all. This will allow us to update d[i][bm OR factor[j]] to d[i-1][bm] + | a[i] - j | accordingly.

Here is the final trick to push down the search space. Let x be the highest possible bitmask such that x AND factor[j] does not intersect. Initially, we set bm equals to x. Then to find all other bm, we can do:
bm := (bm - 1) & x.

Very clever bitmasking technique!

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

/* make use of the prime numbers */
int mark[63];
vector<int> primes;
int a[103];
int dp[103][1<<17];
int par[103][1<<17];
int g[64][18];
int dep[64];
int N;

int iabs(int x){return (x>0?x:-x);}

void print_out(int k,int idx){
    if(k==1){
        printf("%d",par[k][idx]);
        return;
    }
    print_out(k-1,idx^dep[par[k][idx]]);
    printf(" %d", par[k][idx]);
}
int main(){
    for(int i=2;i*i<=60;++i){
        for(int j=i;j*i<=60;++j){
            mark[i*j]=1;
        }
    }
    for(int i=2;i<=60;++i){
        if(!mark[i])primes.push_back(i);
    }
    for(int i=1;i<=60;++i){
        for(int j=0;j<17;++j){
            if(i%primes[j]==0)g[i][j]=1;
        }
        int tmp = 0;
        for(int j=16;j>=0;--j){
            tmp <<= 1;
            tmp += (g[i][j]?1:0);
        }
        dep[i] = tmp;
    }
    scanf("%d", &N);
    for(int i=1;i<=N;++i){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=N;++i){
        for(int j=0;j<(1<<17);++j){
            dp[i][j] = 123456;
        }
    }
    for(int i=1;i<=N;++i){
        for(int j=1;j<=60;++j){
            int bm = ((1<<17)-1)^(dep[j]);
            for(int k=bm;;k=(k-1)&bm){
                int tmp = dp[i-1][k] + iabs(a[i]-j);
                if(tmp < dp[i][k|dep[j]]){
                    dp[i][k|dep[j]] = tmp;
                    par[i][k|dep[j]] = j;
                }
                if(k==0)break;
            }
        }
    }
    int ans = 123456;
    int idx = -1;
    for(int j=0;j<(1<<17);++j){
        if(ans > dp[N][j]){
            ans = dp[N][j];
            idx = j;
        }
    }
    print_out(N, idx);printf("\n");
    return 0;
}

No comments:

Post a Comment