SRM507 Div2 Hard(1000) CubeRoll

CubeRoll

スタートとゴールの間に穴があったら到達不可能。

スタート・ゴールの両側に穴があったら、立方体の移動範囲は狭いので、動的計画法

それ以外の場合はスタートとゴールの距離に着目する。距離が平方数なら1手。距離が平方数の和なら2手。距離が平方数の差なら2手。平方数の差は、2x+1, 4x+4, ……, 2kx+k2の形で表される。ただしxは任意の正数。任意の奇数は平方数の差(2x+1)で表せる。2x+1で移動して1移動すれば任意の偶数の距離を移動できるので、3手あれば充分。

#include <vector>
using namespace std;

class CubeRoll{public:
int getMinimumSteps( int initPos, int goalPos, vector <int> holePos )
{
    //  スタートとゴールの間に穴があったら無理
    for ( int i=0; i<(int)holePos.size(); i++ )
        if ( initPos<holePos[i] && holePos[i]<goalPos  ||
             goalPos<holePos[i] && holePos[i]<initPos )
            return -1;

    //  スタート・ゴールの両側に穴があったら動的計画法
    int HL = 0;
    int HR = 100001;
    for ( int i=0; i<(int)holePos.size(); i++ )
    {
        if ( holePos[i]<initPos && holePos[i]<goalPos )
            HL = max( HL, holePos[i] );
        if ( initPos<holePos[i] && goalPos<holePos[i] )
            HR = min( HR, holePos[i] );
    }

    if ( 0<HL && HR<100001 )
    {
        vector<int> T(HR,-1);
        T[initPos] = 0;
        bool up = true;
        for ( int t=0; up; t++ )
        {
            up = false;
            for ( int i=HL+1; i<=HR-1; i++ )
            if ( T[i] == t )
            {
                for ( int p=1; i+p*p<HR; p++ )
                    if ( T[i+p*p] == -1 )
                        T[i+p*p] = t+1,  up = true;
                for ( int p=1; HL<i-p*p; p++ )
                    if ( T[i-p*p] == -1 )
                        T[i-p*p] = t+1,  up = true;
            }
        }
        return T[goalPos];
    }

    int D = abs(initPos-goalPos);

    //  スタートとゴールの距離が平方数なら1
    for ( int i=1; i*i<=D; i++ )
        if ( i*i == D )
            return 1;

    //  平方数の和で表せるなら2
    for ( int i=1; i*i<=D; i++ )
        for ( int j=i; i*i+j*j<=D; j++ )
            if ( i*i+j*j == D )
                return 2;

    //  平方数の差で表せるなら2
    for ( int i=1; i*i<=D; i++ )
        if ( (D-i*i)%(2*i) == 0 )
            return 2;

    //  それ以外は3
    return 3; 
}};