饮料供货 - 编程之美1.6


《编程之美》1.6 饮料供货
在微软亚洲研究院上班,大家早上来的第一件事是干啥呢?查看邮件?No,是去水房拿饮料:酸奶,豆浆,绿茶、王老吉、咖啡、可口可乐……(当然,还是有很多同事把拿饮料当做第二件事)。
管理水房的阿姨们每天都会准备很多的饮料给大家,为了提高服务质量,她们会统计大家对每种饮料的满意度。一段时间后,阿姨们已经有了大批的数据。某天早上,当实习生小飞第一个冲进水房并一次拿了五瓶酸奶、四瓶王老吉、三瓶鲜橙多时,阿姨们逮住了他,要他帮忙。
从阿姨们统计的数据中,小飞可以知道大家对每一种饮料的满意度。阿姨们还告诉小飞,STC(Smart Tea Corp.)负责给研究院供应饮料,每天总量为V。STC很神奇,他们提供的每种饮料之单个容量都是2的方幂,比如王老吉,都是23=8升的,可乐都是25=32升的。当然STC的存货也是有限的,这会是每种饮料购买量的上限。统计数据中用饮料名字、容量、数量、满意度描述每一种饮料。
那么,小飞如何完成这个任务,求出保证最大满意度的购买量呢?

【解法一】
我们先把这个问题“数学化”一下吧。
假设STC共提供n种饮料,用(SiViCiHiBi)(对应的是饮料名字、容量、可能的最大数量、满意度、实际购买量)来表示第i种饮料(= 0, 1,…, n-1),其中可能的最大数量指如果仅买某种饮料的最大可能数量,比如对于第i中饮料Ci=V/Vi
基于如上公式:
饮料总容量为
总满意度为
那么题目的要求就是,在满足条件41-1副本的基础上,求
对于求最优化的问题,我们来看看动态规划能否解决。用Opt(V'i)表示从第ii+1, i+2, …, n-1种饮料中,算出总量为V'的方案中满意度之和的最大值。
因此,Opt(V0)就是我们要求的值。
那么,我们可以列出如下的推导公式:Opt (V'i) = max { kHi + Opt(V' - Vi * ki+1)}(k = 0, 1, …, Ci,=0, 1, …, n-1)。
即:最优化的结果 = 选择第k种饮料×满意度+减去第k种饮料×容量的最优化结果根据这样的推导公式,我们列出如下的初始边界条件:
Opt(0, n)= 0,即容量为0的情况下,最优化结果为0。
Opt(xn)= -INF(x != 0)(–INF为负无穷大),即在容量不为0的情况下,把最优化结果设为负无穷大,并把它作为初值。
那么,根据以上的推导公式,就不难列出动态规划求解代码,如下所示:
在上面的算法中,空间复杂度为OV*N),时间复杂度约为OV*N*Max(Ci))。
因为我们只需要得到最大的满意度,则计算opt[i][j]的时候不需要opt[i][j+2],只需要opt[k][j+1](0<=k<=V),所以空间复杂度可以降为Ov)。
  1. int Cal(intV, int type)  
  2. {  
  3.     opt[0][T] = 0;// 边界条件  
  4.     for(int i = 1; i <=V; i++)// 边界条件  
  5.     {  
  6.        opt[i][T] = -INF;  
  7.     }  
  8.     for(int j = T - 1; j>= 0; j--)  
  9.     {  
  10.        for(int i = 1; i <= V; i++)  
  11.         {  
  12.            opt[i][j] = -INF;  
  13.            for(int k = 0; k <= C[j];k++)      // 遍历第j种饮料选取数量k  
  14.            {  
  15.                if(i < k * V[j])  
  16.                {  
  17.                    break;  
  18.                }  
  19.                int x = opt[i - k * V[j]][j + 1];  
  20.                if(x != -INF)  
  21.                {  
  22.                    x += H[j] * k;  
  23.                    if(x > opt[i][j])  
  24.                    {  
  25.                        opt[i][j] = x;  
  26.                    }  
  27.                }  
  28.            }  
  29.         }  
  30.     }  
  31.     return opt[V][0];  
  32. }  
【解法二】
应用上面的 动态规划法可以得到结果,那么是否有可能进一步地提高效率呢?我们知道动态规划算法的一个变形是备忘录法,备忘录法也是用一个表格来保存已解决的子问题的 答案,并通过记忆化搜索来避免计算一些不可能到达的状态。具体的实现方法是为每个子问题建立一个记录项。初始化时,该纪录项存入一个特殊的值,表示该子问 题尚未求解。在求解的过程中,对每个待求解的子问题,首先查看其相应的纪录项。若记录项中存储的是初始化时存入的特殊值,则表示该子问题是第一次遇到,此 时计算出该子问题的解,并保存在其相应的记录项中。若记录项中存储的已不是初始化时存入的初始值,则表示该子问题已经被计算过,其相应的记录项中存储的是 该子问题的解答。此时只需要从记录项中取出该子问题的解答即可。
因此,我们可以应用备忘录法来进一步提高算法的效率。

动态规划解法中,前两层循环的次序可以颠倒。也就是在生成的动态数组中,我们可以从上往下一行一行地填,或者从右往左一列一列地填都不影响最后结果,最关键的是需要事先把边界值设定好,最上面一行全为0,最右边一列全为0。
  1. int opt[V + 1][T + 1];      // 子问题的记录项表,假设从i到T种饮料中,  
  2.                                  // 找出容量总和为V’的一个方案,快乐指数最多能够达到  
  3.                                        // opt(V',i,T-1),存储于opt[V’][i],  
  4.                                       // 初始化时opt中存储值为-1,表示该子问题尚未求解。  
  5. int Cal(intV, int type)  
  6. {  
  7.     if(type == T)  
  8.     {  
  9.         if(V== 0)  
  10.            return 0;  
  11.         else  
  12.            return -INF;  
  13.     }  
  14.     if(V < 0)  
  15.        return -INF;  
  16.     else if(V == 0)  
  17.        return 0;  
  18.     else if(opt[V][type] !=-1)  
  19.        return opt[V][type];    // 该子问题已求解,则直接返回子问题的解;  
  20.                                // 子问题尚未求解,则求解该子问题  
  21.     int ret = -INF;  
  22.     for(int i = 0; i <=C[type]; i++)  
  23.     {  
  24.        int temp = Cal(V– i * C[type], type + 1);  
  25.        if(temp != -INF)  
  26.        {  
  27.            temp += H[type] * i;  
  28.            if(temp > ret)  
  29.            ret = temp;  
  30.        }  
  31.     }  
  32.     return opt[V][type] =ret;  
  33. }  
【解法三】
请注意这个题目的限制条件,看看它能否给我们一些特殊的提示。
我们把信息重新整理一下,按饮料的容量(单位为L)排序:
Volume
TotalCount
Happiness
20TC0_0H0_0
20TC0_n0H0_n0
21TC1_0H1_0
 …
2MTCM_0HM_0
 … …
假设最大容量为2 000L。一开始,如果V%(21)非零,那么,我们肯定需要购买20L容量的饮料,至少一瓶。在这里可以使用贪心规则,购买快乐指数最高的一瓶。除去这个,我们只要再购买总量(V-20) L的饮料就可以了。这时,如果我们要购买21L容量的饮料怎么办呢?除了21L容量里面快乐指数最高的,我们还应该考虑,两个容量为20L的饮料组合的情 况。其实我们可以把剩下的容量为20L的饮料之快乐指数从大到小排列,并用最大的两个快乐指数组合出一个新的“容量为2L”的饮料。不断地这样使用贪心原 则,即得解。这是不是就简单了很多呢?
书中的贪心解法似曾相识,把信息按照饮料的容量排序(其中设我们有n0种容量为20的饮料):
Volume
TotalCount
Happiness
20TC0_0H0_0
20TC0_n0H0_n0
21TC1_0H1_0
 
2MTCM_0HM_0
  
然后按照下面的顺序进行贪心选择:
(1) 饮料总量为1,从容量为20的饮料中选出快乐指数最大的。
(2) 饮料总量为2,从容量为21的饮料中选出快乐指数最大的(设为H1),与容量为20的饮料中快乐指数最大的(设为H0),比较H12* H0,取出其中最大者为当前最佳选择
(3) 继续进行下去,直到求出OptV0
粗看一下,有些似曾相识。我们在买书问题的时候也曾面临将买书计划拆分,然后查表进行贪心选择。然而买书问题每次选择至多选M本书,而且每次选择只影响下一次选择,所以只需要把2M进行有限的拆分即可。
而本题则不尽相同,对于某种容量V’(以V’=11为例)来说,有两个问题:
1. 首先,我们需要察看所有拆分的可能性,找出其中最大者作为本次贪心选择的结果。其中,由于“每种饮料的单位容量都是2的方幂”,所以拆分结果仅考虑用小于V’2的方幂来进行组合,即(计算式111=8+2+14+4+2+14+2+2+2+1….1+1+…+1。可以看到,对于V’我们至少可以“拆”出V’种组合(或许更多),即便我们把每次的计算结果用表格保存起来,我们的查找次数也至少是Ω(1+2+…+V’=V’2),空间复杂度也很高,并没有如数中说“简单很多”。而且,如果取消“每种饮料的单位容量都是2的方幂”的限制,拆分的结果就会变成(计算式211=10+19+25+55+5+15+4+2…..,导致查找次数进一步增加。
2.  其次,让我们回到贪心选择的定义上来。由于贪心选择性,贪心算法的过程是每次选择都选取当前看来最好的结果,使得当达到最终状态时的结果刚好是最优解。而我们再看V’=11的例子,假设最优解是4+4+2+1,则我们曾经计算过的8就不是最优解的一部分,这就和贪心算法的精神不符,所以这个方法其实还是动态规划。

书中提到第二种解法,贪心算法。由于所有饮料的容量都是2的整数幂,这就给贪心创造了条件。
假设给定的容量为V,我们可以把V写成二进制的形式,不妨设V=7,二进制的写法为111。
接下来我们就从最低位开始取:
第一步,取第一个1:拿出容量为1满意度最大的饮料,
第二步,取第二个1:使用剩余容量为1的饮料构造容量为2的饮料,比如(1,2)(1,3)构造出(2,5),并从新构造的和原有容量为2的所有饮料中选出满意度最大的。
第三步,取第三个1:递归构造容量为4的饮料,并从新构造的和原有容量为4的所有饮料中选出满意度最大的。
  1. int TakeOutMax( int k)  
  2. {  
  3.     int maxHappiness = 0;  
  4.     if (k < 0) return 0;  
  5.       
  6.     if (k > 0){  
  7.         int t =k/2;  
  8.         int h = TakeOutMax(t);  
  9.         if (h>0){  
  10.             drinks[t].push_back(Drink((int)pow(2,t), 1, h));  
  11.             h = TakeOutMax(t);  
  12.             if (h>0)  
  13.             {  
  14.                 drinks[t].push_back(Drink((int)pow(2,t), 1, h));  
  15.             }  
  16.         }  
  17.         int p1, p2;  
  18.         p1 = p2 = -1;  
  19.         for (int i=0; i<drinks[t].size(); i++)  
  20.         {  
  21.             for (int j=i+1; j<drinks[t].size(); ++j)  
  22.             {  
  23.                 if (drinks[t][i].TotalCount>0 && drinks[t][j].TotalCount>0 && drinks[t][i].Happiness+drinks[t][j].Happiness>maxHappiness)  
  24.                 {  
  25.                     maxHappiness = drinks[t][i].Happiness+drinks[t][j].Happiness;  
  26.                     p1 = i;  
  27.                     p2 = j;  
  28.                 }  
  29.             }  
  30.         }  
  31.         if (p1 >-1 && p2 > -1)  
  32.         {  
  33.             drinks[t][p1].TotalCount--;  
  34.             drinks[t][p2].TotalCount--;  
  35.             drinks[k].push_back(Drink((int)pow(2,k), 1, maxHappiness));  
  36.         }  
  37.     }  
  38.     int p=-1;  
  39.     maxHappiness = 0;  
  40.     for (int i=0; i<drinks[k].size(); ++i)  
  41.     {  
  42.         if (drinks[k][i].TotalCount>0 && drinks[k][i].Happiness>maxHappiness)  
  43.         {  
  44.             maxHappiness = drinks[k][i].Happiness;  
  45.             p = i;  
  46.         }  
  47.     }  
  48.     if (p >=0 ){  
  49.         drinks[k][p].TotalCount--;  
  50.     }  
  51.     return maxHappiness;  
  52. }  
  53.   
  54. // 贪心算法  
  55. int GetMaxHappinessByGreed(int V)  
  56. {  
  57.     int k = V;  
  58.     int i=0;  
  59.     int happiness = 0;  
  60.     while(k)  
  61.     {  
  62.         if (k & 1) happiness += TakeOutMax(i);  
  63.         k >>= 1;  
  64.         i++;  
  65.     }  
  66.     return happiness;  
  67. }  
http://bylijinnan.iteye.com/blog/1601908
  1.      * 设Opt(V’,i)表示从i到n-1种饮料中,总容量为V’的方案中,满意度之和的最大值。 
  2.      * 那么递归式就应该是:Opt(V’,i)=max{ k * Hi+Opt(V’-Vi * k,i+1)}(k=0,1,2…,Ci,i=0,1,2…,n-1) 
  3.      * 其中Ci为第i种饮料的最大供应量 
  4.      *  
  5.      * 想了好久都没法理解书上这个递推公式里面的i+1,为什么是i+1而不是其他? 
  6.      * 后来我转换了一下,代码本质上是一样的,只是比较符合我自己的思路: 
  7.      * opt[i][j] i代表供货总容量,j代表饮料种类。假设容量都是整数。i和j都从1开始考虑: 
  8.      * 当只有一种饮料时,容易求得各种总容量对应的最大满意度;当新增加一种饮料时,将一部分容量用新饮料代替,求得新的满意度; 
  9.      * 将新的满意度与旧满意度比较,如果新结果较大就更新。 
  10.      * 如何由opt[i][j-1]求得opt[i][j]呢? 
  11.      * --将一部分容量分给第j种饮料,那新的满意度就是(opt[i-k*v][j]+k*value),其中v代表第j种饮料每瓶的容量,k代表瓶数,value代表满意度  
  12.     public int maxValue(Beverage[] beverages,int V,int T){  
  13.           
  14.         if(beverages==null||beverages.length==0){  
  15.             return -1;  
  16.         }  
  17.         if(V<0){  
  18.             return -1;  
  19.         }  
  20.         if(T<0 || T>beverages.length){        //beverages' index starts with 1  
  21.             return -1;  
  22.         }  
  23.           
  24.         int[][] opt=new int[V+1][T+1];  
  25.       
  26.         for(int j=1;j<=T;j++){  
  27.             int c=beverages[j].count;  
  28.             int v=beverages[j].volume;  
  29.             int value=beverages[j].value;  
  30.             for(int i=1;i<=V;i++){  
  31.                 for(int k=0;k<=c;k++){  
  32.                     if(i<k*v){  
  33.                         break;  
  34.                     }  
  35.                     int x=opt[i-k*v][j-1];  
  36.                     int y=x+k*value;  
  37.                     if(y>=opt[i][j]){  
  38.                         opt[i][j]=y;  
  39.                     }  
  40.                 }  
  41.             }  
  42.         }  
  43. //      myprint(opt);  
  44.         return opt[V][T];  
  45.     }  
  46.       
  47.     static class Beverage{  
  48.         int value;      //满意度  
  49.         int volume;     //每瓶的容量  
  50.         int count;      //最多可提供多少瓶  
  51.         Beverage(int value,int volume,int count){  
  52.             this.value=value;  
  53.             this.volume=volume;  
  54.             this.count=count;  
  55.         }  
  56.     } 
http://www.cnblogs.com/gaopeng527/p/4604079.html
    public int cal(int v, int T){
        int i,j,k;
        //opt[v][i]表示从i,...,t种饮料中,算出总容量为v的方案的满意度之和的最大值
        int[][] opt; 
        opt = new int[v+1][T+1];    
        opt[0][T] = 0;  //边界条件
        for(i=1;i<=v;i++){
            opt[i][T]=-INF;  //边界条件
        }
        for(j=T-1;j>=0;j--){
            for(i=0;i<=v;i++){
                opt[i][j] = -INF;
                for(k=0;k<=count[j];k++){  //遍历第j种饮料选取数量k
                    if(i<=k*V[j]){
                        break;
                    }
                    int x = opt[i-k*V[j]][j+1];
                    if(x!=-INF){
                        x += k*h[j];
                        if(x > opt[i][j]){
                            opt[i][j]=x;
                        }
                    }
                }
            }
        }
        return opt[v][0];
    }

【解法二】备忘录法

    public int cal(int v, int t){
        int i,j;
        //opt[v][i]表示从i,...,t种饮料中,算出总容量为v的方案的满意度之和的最大值
        int[][] opt; //子问题的记录项表,初始化时opt中存储值为-1,表示该子问题尚未求解
        opt = new int[v+1][T+1];
        for(i=0;i<=v;i++){ //初始化opt, this should be put outside
            for(j=0;j<=t;j++){
                opt[i][j] = -1;
            }
        }
        if(t == T){
            if(v == 0)
                return 0;
            else
                return -INF;
        }
        if(v < 0)
            return -INF;
        else if(v == 0)
            return 0;
        else if(opt[v][t]!=-1){ //该子问题已求解,直接返回子问题的解
            return opt[v][t];  
        }
        //子问题尚未求解,则求解子问题
        int result = -INF;
        for(i=0;i<count[t];i++){
            int temp = cal(v-i*V[t],t+1);
            if(temp != INF){
                temp += i*h[t];
                if(temp>result)
                    result = temp;
            }
        }
        return opt[v][t] = result;
    }

【解法三】 贪心算法





Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts