Problem:
Score : 300 points

Problem Statement

Takahashi is standing on a multiplication table with infinitely many rows and columns.

The square (i,j) contains the integer i \times j. Initially, Takahashi is standing at (1,1).

In one move, he can move from (i,j) to either (i+1,j) or (i,j+1).

Given an integer N, find the minimum number of moves needed to reach a square that contains N.

Constraints

2 \leq N \leq 10^{12}

N is an integer.

Input

Input is given from Standard Input in the following format:

N

Output

Print the minimum number of moves needed to reach a square that contains the integer N.

Sample Input 1

10

Sample Output 1

5

(2,5) can be reached in five moves. We cannot reach a square that contains 10 in less than five moves.

Sample Input 2

50

Sample Output 2

13

(5, 10) can be reached in 13 moves.

Sample Input 3

10000000019

Sample Output 3

10000000018

Both input and output may be enormous.

Solution in C# (Mono 4.6.2.0) (judge verdict: Time Limit Exceeded)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class Program
{

        static System.Diagnostics.Stopwatch sw;
        static List<string> lstMsg;

    static void Main(string[] args)
    {
            sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            lstMsg = new List<string>();

            // 前準備
            List<int> lstPrim = GetPrim();
            //sw.Stop();
            //lstMsg.Add(sw.Elapsed.ToString());
            //sw.Restart();

            // 入力
            string[] sS = System.Console.ReadLine().Trim().Split(' ');
            long iN = long.Parse(sS[0]);

            sw.Stop();
            lstMsg.Add(sw.Elapsed.ToString());
            sw.Restart();

            // 解析
            List<List<long>> lstBunk = GetBunkai(lstPrim, iN);                  // 2つの約数に分解
            List<long> lstDf = new List<long>();
            for (int i = 0; i < lstBunk.Count; i++)
            {
                lstDf.Add(lstBunk[i][0] - 1 + lstBunk[i][1] - 1);
            }
            lstDf.Sort();

            // 出力
            System.Console.WriteLine("{0}", lstDf[0].ToString().Trim());

            sw.Stop();
#if DEBUG
            Console.WriteLine();
            //Console.WriteLine("素数取得:{0}", lstMsg[0].ToString());
            //Console.WriteLine("入力時間:{0}", lstMsg[1].ToString());
            //Console.WriteLine("因数分前:{0}", lstMsg[2].ToString());
            //Console.WriteLine("因数分解:{0}", lstMsg[3].ToString());
            //Console.WriteLine("組合取得:{0}", lstMsg[4].ToString());
            //Console.WriteLine("約数取得:{0}", lstMsg[5].ToString());
            Console.WriteLine("後の処理:{0}", sw.Elapsed.ToString());
            Console.WriteLine("続行するには何かキーを押してください．．．");
            Console.ReadKey();
#endif
    }

        // 前準備 1000000以下の素数取得
        private static List<int> GetPrim()
        {
            // 前準備 1000000以下の素数取得
            int iMax = 1000000;   // 1000以下の素数が既知であるのが前提
            List<int> lstPrm = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, };
            List<Boolean> lstMan = new List<Boolean>();
            for (int i = 0; i <= iMax; i++) { lstMan.Add(true); }
            lstMan[0] = false; lstMan[1] = false;
            for (int i = 0; i < lstPrm.Count; i++) { for (int j = 2; j <= iMax / lstPrm[i]; j++) { lstMan[lstPrm[i] * j] = false; } }
            List<int> lstNew = new List<int>();
            for (int i = 0; i < lstMan.Count; i++) { if (lstMan[i]) { lstNew.Add(i); } }
            return lstNew;
        }

        // 2つに分解
        private static List<List<long>> GetBunkai(List<int> lstPrim, long iNNN)
        {
            List<long> lstFct = new List<long>();                                                   // 素因数分解要素
            int i = 0;
            int iEnd = lstPrim.Count;
            long iNN = iNNN;
            long iSqr = (long)Math.Sqrt(iNN);

            //sw.Stop();
            //lstMsg.Add(sw.Elapsed.ToString());
            //sw.Restart();

            while (i < iEnd)
            {
                if (iNN % lstPrim[i] == 0) { iNN /= lstPrim[i]; lstFct.Add(lstPrim[i]); }           // 素因数分解
                else { i += 1; }
                if (i >= iEnd) { break; }
                if (lstPrim[i] > iNN) { break; }
            }

            //sw.Stop();
            //lstMsg.Add(sw.Elapsed.ToString());
            //sw.Restart();

            int iN = lstFct.Count;                                                                  // 素因数の数
            List<ClsYak> lstSin2 = GetSin2(lstFct);                                                 // 素因数分解(素数が何個か情報)
            List<ClsYak> lstAlls = new List<ClsYak>();                                              // ある組合せ結果の全部
            List<List<long>> lstBunk = new List<List<long>>();                                      // ２分割要素
            for (int j = 1; j <= iN; j++)                                                           // 全素因数から1個からN個までの組合せ全部試す
            {
                int iK = j;
                List<ClsYak> lstComp = new List<ClsYak>();                                          // 今回追加組合せ結果
                GetDupCo2(lstAlls, lstComp, lstSin2, iK);                                           // 今回追加組合せ取得  素因数組
                DelDupCom(lstAlls, lstComp);                                                        // 既に存在する値は除外
            }

            //sw.Stop();
            //lstMsg.Add(sw.Elapsed.ToString());
            //sw.Restart();

            for (int j = 0; j < lstAlls.Count; j++)                                                 // 1個からN個までの全組合せ
            {
                long iDiv = lstAlls[j].iA;
                long iDv2 = iNNN / iDiv;
                lstBunk.Add(new List<long>() { iDiv, iDv2 });
            }
            lstBunk.Add(new List<long>() { 1L, iNNN });                                             // 1 x iNNN の分解をプラス

            //sw.Stop();
            //lstMsg.Add(sw.Elapsed.ToString());
            //sw.Restart();

            //DebugDisp(lstAlls);
            return lstBunk;
        }

        // Debug表示
        private static void DebugDisp(List<ClsYak> lstAlls)
        {
            for (int j = 0; j < lstAlls.Count; j++)
            {
                string sOut = "";
                for (int k = 0; k < lstAlls[j].iP.Count; k++)
                {
                    sOut += lstAlls[j].iP[k].ToString() + " ";
                }
                sOut += lstAlls[j].iA.ToString() + " ";
                System.Console.WriteLine("{0}", sOut.ToString().Trim());
            }
        }

        // 素因数分解情報生成
        private static List<ClsPrm> GetSoin(List<long> lstFct)
        {
            List<ClsPrm> lstSoin = new List<ClsPrm>();
            long iBak = 0;
            for (int j = 0; j < lstFct.Count; j++)
            {
                if (lstFct[j] != iBak)
                {
                    lstSoin.Add(new ClsPrm() { iP = lstFct[j], iC = 1 });
                }
                else
                {
                    lstSoin[lstSoin.Count - 1].iC += 1;
                }
                iBak = lstFct[j];
            }
            return lstSoin;
        }

        // 素因数分解情報生成2
        private static List<ClsYak> GetSin2(List<long> lstFct)
        {
            List<ClsYak> lstSoin = new List<ClsYak>();
            long iBak = 0;
            for (int j = 0; j < lstFct.Count; j++)
            {
                if (lstFct[j] != iBak)
                {
                    ClsYak oneUsr = new ClsYak();
                    oneUsr.iP.Add(lstFct[j]);
                    lstSoin.Add(oneUsr);
                }
                else
                {
                    lstSoin[lstSoin.Count - 1].iP.Add(lstFct[j]);
                }
                iBak = lstFct[j];
            }
            return lstSoin;
        }

        // 重複組合せ取得その２
        private static void GetDupCo2(List<ClsYak> lstAlls, List<ClsYak> lstComp, List<ClsYak> lstSin2, int iK)
        {
            switch (iK)
            {
                case 1:     // 1個の時
                    for (int i = 0; i < lstSin2.Count; i++)
                    {
                        ClsYak oneUsr = new ClsYak();
                        oneUsr.iP.Add(lstSin2[i].iP[0]);
                        oneUsr.iA = lstSin2[i].iP[0];
                        lstComp.Add(oneUsr);
                    }
                    break;
                default:     // 2個以上の時
                    for (int i = 0; i < lstAlls.Count; i++)
                    {
                        for (int j = 0; j < lstSin2.Count; j++)
                        {
                            ClsYak oneUsr = new ClsYak();
                            oneUsr.iP.AddRange(lstAlls[i].iP);
                            oneUsr.iA = lstAlls[i].iA;

                            if (oneUsr.iP[oneUsr.iP.Count - 1] < lstSin2[j].iP[0]) { continue; }
                            int iCnt = 0;
                            for (int k = 0; k < oneUsr.iP.Count; k++)
                            {
                                if (oneUsr.iP[k] == lstSin2[j].iP[0]) { iCnt += 1; }
                            }
                            if (lstSin2[j].iP.Count > iCnt)
                            {
                                oneUsr.iP.Add(lstSin2[j].iP[0]);
                                oneUsr.iA *= lstSin2[j].iP[0];
                                IsEquation(lstComp, oneUsr);
                            }
                        }
                    }
                    break;
            }
        }

        // 同値確認
        private static Boolean IsEquation(List<ClsYak> lstComp, ClsYak oneUsr)
        {
            // 総積の比較
            Boolean bIsEx = false;
            for (int j = 0; j < lstComp.Count; j++)
            {
                if (lstComp[j].iA == oneUsr.iA) { bIsEx = true; break; }
            }
            if (!bIsEx) { lstComp.Add(oneUsr); }
            return bIsEx;
        }

        // 既存と同じのを削除
        private static void DelDupCom(List<ClsYak> lstAlls, List<ClsYak> lstComp)
        {
            List<int>  lstAdd = new List<int>();
            for (int i = 0; i < lstComp.Count; i++)
            {
                Boolean bIsEx = false;
                for (int p = 0; p < lstAlls.Count; p++)
                {
                    if (lstAlls[p].iA == lstComp[i].iA) { bIsEx = true; break; }
                }
                if (!bIsEx) { lstAdd.Add(i); }
            }
            if (lstAdd.Count > 0)
            {
                for (int i = 0; i < lstAdd.Count; i++)
                {
                    ClsYak oneUsr = new ClsYak();
                    oneUsr.iP.AddRange(lstComp[lstAdd[i]].iP);
                    oneUsr.iA = lstComp[lstAdd[i]].iA;
                    lstAlls.Add(oneUsr);
                }
            }
        }

        // 素因数情報
        private class ClsPrm
        {
            public long iP { get; set; } = 0L;
            public int iC { get; set; } = 0;
        }

        // 素因数情報
        private class ClsDeb
        {
            public long iA { get; set; } = 0L;
            public long iB { get; set; } = 0L;
        }

        // 約数情報
        private class ClsYak
        {
            public List<long> iP { get; set; } = new List<long>();          // 素数列挙
            public long       iA { get; set; } = 0L;                        // 総積
        }

}