問題
http://community.topcoder.com/stat?c=problem_statement&pm=11434&rd=14437
数字の列と、現在の位置が与えられる。
2つの操作のいずれかによって、位置を1番目に移動させたい。
・位置を右か左に移動
・現在の列数の素数で割り、列の長さを列/素数に変換
このときの最小の操作回数を求める。
解き方
N,Mが大きいのでdpでは配列を確保できない。
今回2つの匝瑳のうち、
移動の操作は割る操作よりも後で行えばよいので
各列についてたかだか1回の操作でよい。
よって各列について割る操作すべてについてBFSで調べていき、
また移動についても各列1度のみ調べていけばよい。
コード
class DivideAndShift {
public:
int getLeast(int N, int M) {
int ret=1e+9;
int prime[N+1];
memset(prime,1,sizeof(prime));
prime[0]=0,prime[1]=0;
for(int i=2;i<=N;i++)if(prime[i]){
for(int j=i+i;j<=N;j+=i)prime[j]=0;
}
queue<pair<pair<int,int>,int> > q;
q.push(make_pair(make_pair(N,M-1),0));
while(!q.empty()){
int x=q.front().first.first;
int pos=q.front().first.second;
int turn=q.front().second;
q.pop();
ret=min(ret,turn+min(pos,x-pos));
vector<int> vx;
for(int i=2;i*i<=x;i++)if(x%i==0){
if(prime[i])vx.push_back(i);
if(prime[x/i])vx.push_back(x/i);
}
if(prime[x])vx.push_back(x);
FORE(i,0,vx.size()){
int len=x/vx[i];
q.push(make_pair(make_pair(len,pos%len),turn+1));
}
}
return ret;
}
};
public:
int getLeast(int N, int M) {
int ret=1e+9;
int prime[N+1];
memset(prime,1,sizeof(prime));
prime[0]=0,prime[1]=0;
for(int i=2;i<=N;i++)if(prime[i]){
for(int j=i+i;j<=N;j+=i)prime[j]=0;
}
queue<pair<pair<int,int>,int> > q;
q.push(make_pair(make_pair(N,M-1),0));
while(!q.empty()){
int x=q.front().first.first;
int pos=q.front().first.second;
int turn=q.front().second;
q.pop();
ret=min(ret,turn+min(pos,x-pos));
vector<int> vx;
for(int i=2;i*i<=x;i++)if(x%i==0){
if(prime[i])vx.push_back(i);
if(prime[x/i])vx.push_back(x/i);
}
if(prime[x])vx.push_back(x);
FORE(i,0,vx.size()){
int len=x/vx[i];
q.push(make_pair(make_pair(len,pos%len),turn+1));
}
}
return ret;
}
};