HDU 3860 Naughty fairies ( C++ 代码 YY策略 + 高精度 )

  这道题,首先想到的就是BFS,但是因为数据太大,BFS不仅TLE而且MLE。

我是这么思考的:

如果n >= m 显然只能是1步1步减,输出n-m即可。

如果m > n ,也就是说m经过若干步骤后变成了n。

我们假设m的变化中第2次变化有*2操作,那么在下面的情况里不可能出现相邻连续+1+1或者-1-1,如果出现,只要先+1或-1再*2即可。

eg. 6 ->14 6 * 2 = 12   12 + 1 + 1 = 14 ; 3步

6 + 1 = 7 7 * 2 = 14 ; 2步

有了这一点,不难想到此题我们考虑从n到m,一开始只有1个偶数(或者奇数)扩展相邻项后最多每层只有2个偶数,那么一共有log(2,1e500)层(不到2000层),

每层2个数,那就没压力了。然后记下每层的到达这个数的最小步数(相邻则进行更新)。


eg. m = 801               n = 5

第一层    800  801  802

步数  1 0  1           更新ans = 801 -5=796

第二层  400  402     更新ans

……

交后的测试期望时间是78MS


<pre name="code" class="cpp">/*
 *    Author:
 *        OpenPandora 
 *     Date:
 *          2014.08.26
 */ 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define cls(a) memset(a,0,sizeof(a))
#define rise(i,a,b) for(int i=a;i<=b;i++)
#define fall(i,a,b) for(int i=a;i>=b;i--)
const int base = 1e9;
char a[600];
bool ined, flag;
int cnt, cntt, ep;
void init()
{
    ined = false; cnt = 0;    
    while( true )
    {
        a[cnt] = getchar();
        if( a[cnt] >= '0' && a[cnt] <= '9' ) 
        {
            ined = true;
            cnt ++;    
        }
        else if( ined ) break;
    }
    a[cnt] = '\0';
    cnt --;
}
struct point
{
    int num[60], len;
    point() { clear(); }
    void clear(){ len = 1 ; cls(num); }
    void out( int x ) 
    { 
        printf( "%d" , num[len] );
        fall( i , len - 1 , 1 ) printf( "%09d" , num[i] );
        if( x )putchar('\n');
        else putchar( ' ' );
    }
    void in()
    {
        init(); cntt = 0; ep = 1; clear();
        fall( i , cnt , 0 )
        {
            if( cntt == 9 )
            {
                cntt = 0;
                len ++;
                ep = 1;
            }
            cntt ++;
            num[len] += (int) ( a[i] - '0' ) * ep;
            ep *= 10;
        }
        while( len > 1 && num[len] == 0 ) len --; 
    }
    void inc()
    {
        num[1] ++;
        rise( i , 1 , len )
        {
            if( num[i] >= base )
            {
                num[i+1] ++;
                num[i] -= base;
            }
            else break;
        }
        if( num[len+1] == 1 ) len ++;
    }
    void dec( int x )
    {
        num[1] -=x;
        rise( i , 1 , len )
        {
            if( num[i] < 0 )
            {
                num[i+1] --;
                num[i] += base;
            }
            else break;
        }
        if( num[len] == 0 ) len --;
    }
    void divid()
    {
        fall( i , len , 1 )
        {
            num[i-1] += ( num[i] & 1 ) * base;
            num[i] >>= 1;
        }
        if( num[len] == 0 ) len --;
    }
    point &operator = ( const point& ); 
    point operator - ( const point& )const; 
    point operator + ( const point& )const;
    point(const int);
    bool operator>(const point &p )const;
    bool operator<(const point &p )const;
    bool operator==(const point &p )const;
}n, m, one, zero, two, now[3], tmp[5], nv[3], tv[5], ans;
point & point :: operator=(const point &p)
{
    clear();
    len = p.len;
    rise( i , 1 , len ) num[i] = p.num[i];
    return *this;
}
point point ::operator+(const point &p)const
{
    point ret = *this;
    int l = max( ret.len , p.len );
    rise( i , 1 , l )
    {
        ret.num[i] += p.num[i];
        if( ret.num[i] >= base ) 
        {
            ret.num[i+1] ++;
            ret.num[i] -= base;
        }
    }
    if( ret.num[l+1] > 0 ) l ++;
    ret.len = l;
    return ret;
}
point point ::operator-(const point &p)const
{
    point ret = *this;
    if( ret > p )
    {
        rise( i , 1 , ret.len )        
        {
            ret.num[i] -= p.num[i];
            if( ret.num[i] < 0 ) 
            {
                ret.num[i] += base;
                ret.num[i+1] --;
            }
        }
        while( ret.len > 1 )
        if( ret.num[ret.len] == 0 ) ret.len --;
        else break;
        return ret;
    }
    if( ret == p )
    return zero;
    if( ret < p )
    {
        point tmp = ret;
        ret = p;
        rise( i , 1 , ret.len )        
        {
            ret.num[i] -= tmp.num[i];
            if( ret.num[i] < 0 ) 
            {
                ret.num[i] += base;
                ret.num[i+1] --;
            }
        }
        while( ret.len > 1 )
        if( ret.num[ret.len] == 0 ) ret.len --;
        else break;
        return ret;
    }
}
point::point( const int b )
{
    clear();
    num[1] = b;
}
bool point::operator==(const point &p)const
{
    if( len != p.len ) return false;
    fall( i , len , 1 ) if( num[i] != p.num[i] ) return false;
    return true;
}
bool point::operator>(const point &p)const
{
    if( len > p.len ) return true;
    if( len < p.len ) return false;
    fall( i , len , 1 ) 
    {
        if( num[i] > p.num[i] ) return true;
        if( num[i] < p.num[i] ) return false;
    }
    return false;
}
bool point::operator<(const point &p)const
{
    if( len < p.len ) return true;
    if( len > p.len ) return false;
    fall( i , len , 1 ) 
    {
        if( num[i] < p.num[i] ) return true;
        if( num[i] > p.num[i] ) return false;
    }
    return false;
}

int main()
{    
    one = point(1);
    zero = point(0);
    two = point(2);
    int cnt = 1;
    while( true )
    {
        m.in();
        n.in();
        if( n == zero && m == zero ) break;
        if( n > m || n == m )
        {
            ( n - m ).out(1);
            continue;
        }
        now[1] = m;
        ans = m - n;
        cnt = 1;cntt=0;
        while( cnt )
        {
            rise( i , 1 , cnt )
            {
                if( ans > now[i] - n + nv[i] ) ans = now[i] - n + nv[i];
                if( now[i].num[1] % 2 == 0 )
                {
                    flag = 0;
                    rise( j , 1 , cntt )
                    if( now[i] == tmp[j] )
                    {
                        if( nv[i] < tv[j] )
                        tv[j] = nv[i];
                        flag = true;
                    }
                    if( !flag )
                    {
                        tmp[++cntt] = now[i];
                        tv[cntt] = nv[i];
                    }
                }
                else
                {
                    nv[i].inc();
                    flag = false;
                    now[i].inc();
                    rise( j , 1 , cntt )
                    if( now[i] == tmp[j] )
                    {
                        if( nv[i] < tv[j] )
                        tv[j] = nv[i];
                        flag = true;
                    }
                    if( !flag )
                    {
                        tmp[++cntt] = now[i];
                        tv[cntt] = nv[i];
                    }
                    
                    if( now[i] > two ) 
                    {
                        flag = false;
                        now[i].dec(2);
                        rise( j , 1 , cntt )
                        if( now[i] == tmp[j] )
                        {
                            if( nv[i] < tv[j] )
                            tv[j] = nv[i];
                            flag = true;
                        }
                        if( !flag )
                        {
                            tmp[++cntt] = now[i];
                            tv[cntt] = nv[i];
                        }
                    }
                }
                now[i].clear();
                nv[i].clear();
            }
            cnt = 0;
            rise( i , 1 , cntt )
            {
                if( ( tmp[i].len > 1 || tmp[i].num[1] > 2 ) && tmp[i] > n )
                {
                    tmp[i].divid();
                    nv[++cnt] = tv[i] + one;
                    now[cnt] = tmp[i];
                }
                tmp[i].clear();
                tv[i].clear();
            }
            cntt = 0;
        }
        ans.out(1);
    }
    return 0;
}



                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值