Algorithms-UCSanDiego

Data Structures and Algorithms by UCSanDiego

View project on GitHub

2. Primitive Calculator

Problem

Solutions

CPP

    #include <iostream>
    #include <sstream>
    #include <iterator>
    #include <algorithm>
    #include <queue>
    #include <unordered_map>
    #include <cassert>

    using namespace std;

    using Type = int;
    using Memo = unordered_map< Type,Type >;
    using Collection = deque< Type >;

    const auto INF = static_cast< Type >( 1e6+1 );

    Collection reconstruct( Type N, Memo& memo, Collection A={} ){
        while( 0 < N ){
            A.push_front( N );
            auto prev3 = ( N % 3 == 0 && memo.find( N / 3 ) != memo.end() )? memo[ N / 3 ] : INF;
            auto prev2 = ( N % 2 == 0 && memo.find( N / 2 ) != memo.end() )? memo[ N / 2 ] : INF;
            auto prev1 = ( N - 1 >= 0 && memo.find( N - 1 ) != memo.end() )? memo[ N - 1 ] : INF;
            auto prev = min({ prev3, prev2, prev1 });
            if(      prev == prev3 ) N /= 3;
            else if( prev == prev2 ) N /= 2;
            else if( prev == prev1 ) N -= 1;
        }
        return A;
    }

    namespace TopDown {
        template< typename Type >
        class Solution {
        public:
            Collection minOps( Type N, Memo memo={}, Collection A={} ){
                go( N, memo );
                return reconstruct( N, memo );
            }
        private:
            Type go( Type N, Memo& memo, Type ans=INF ){
                if( N < 2 )
                    memo[ N ] = 0;
                if( memo.find( N ) != memo.end() )
                    return memo[ N ];
                if( N % 3 == 0 ) ans = min( ans, 1 + go( N / 3, memo ));
                if( N % 2 == 0 ) ans = min( ans, 1 + go( N / 2, memo ));
                return memo[ N ] = min( ans, 1 + go( N - 1, memo ));
            }
        };
    }
    namespace BottomUp {
        template< typename Type >
        class Solution {
        public:
            Collection minOps( Type N, Memo memo=1 ){
                Collection dp( N+1, INF );
                dp[ 1 ] = 0;
                for( auto i{ 2 }; i <= N; ++i ){
                    if( i % 3 == 0 ) dp[ i ] = min( dp[ i ], 1 + dp[ i / 3 ] );
                    if( i % 2 == 0 ) dp[ i ] = min( dp[ i ], 1 + dp[ i / 2 ] );
                    memo[ i ] = dp[ i ] = min( dp[ i ], 1 + dp[ i - 1 ] );
                }
                return reconstruct( N, memo );
            }
        };
    }

    int main() {
        BottomUp::Solution< Type > dp_solution;
        auto N{ 0 }; cin >> N;
        auto A = dp_solution.minOps( N );
        cout << (( A.empty() )? 0 : A.size() - 1 ) << endl;
        copy( A.begin(), A.end(), ostream_iterator< Type >( cout, " " )); cout << endl;
        TopDown::Solution< Type > rec_solution;
        auto A1 = rec_solution.minOps( N );
        assert( A1 == A );
        return 0;
    }

Python3

    from typing import List, Dict

    Type = int
    INF = 1000001
    Memo = Dict[ Type,Type ]
    Collection = List[ Type ]

    def reconstruct( N: Type, memo: Memo={}, A: Collection=[] ) -> Collection:
        while 0 < N:
            A.insert( 0, N )
            prev3 = memo[ N // 3 ] if N % 3 == 0 and N // 3 in memo else INF
            prev2 = memo[ N // 2 ] if N % 2 == 0 and N // 2 in memo else INF
            prev1 = memo[ N - 1  ] if N - 1 >= 0 and N - 1  in memo else INF
            prev = min( prev3, prev2, prev1 )
            if prev == prev3:
                N //= 3
            elif prev == prev2:
                N //= 2
            elif prev == prev1:
                N -= 1
        return A

    class RECSolution:
        def minOps( self, N: Type, memo: Memo={} ) -> Type:
            self.go( N, memo )
            return reconstruct( N, memo )
        def go( self, N: Type, memo: Memo, ans: Type=INF ) -> Type:
            if N < 2:
                memo[ N ] = 0
            if N in memo:
                return memo[ N ]
            if N % 2 == 0:
                ans = min( ans, 1 + self.go( N // 2, memo ))
            if N % 3 == 0:
                ans = min( ans, 1 + self.go( N // 3, memo ))
            memo[ N ] = min( ans, 1 + self.go( N - 1, memo ))
            return memo[ N ]
                                
    class DPSolution:
        def minOps( self, N: Type, memo: Memo={1:0} ) -> Type:
            dp = [ INF ] * ( N+1 )
            dp[ 1 ] = 0
            for i in range( 2, N+1 ):
                if i % 2 == 0:
                    dp[ i ] = min( dp[ i ], 1 + dp[ i // 2 ] )
                if i % 3 == 0:
                    dp[ i ] = min( dp[ i ], 1 + dp[ i // 3 ] )
                memo[ i ] = dp[ i ] = min( dp[ i ], 1 + dp[ i - 1 ] )
            return reconstruct( N, memo )

    if __name__ == '__main__':
        dp_solution = DPSolution()
        N = int( input() )
        A = dp_solution.minOps( N )
        print( len(A) - 1 )
        print( A )
        rec_solution = RECSolution()
        A1 = rec_solution.minOps( N )
        assert( A1 == A )