[NOIP2004] - 洛谷P1090 合并果子 Monotone Queue


https://www.cnblogs.com/neverforget/archive/2011/10/13/ll.html
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将 1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为 12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
【输入文件】
输入文件fruit.in包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。
【输出文件】
输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。
【样例输入】
3
1 2 9
【样例输出】
15
【数据规模】
对于30%的数据,保证有n <= 1000;
对于50%的数据,保证有n <= 5000;
对于全部的数据,保证有n <= 10000。

这个题目非常的经典,发放也很多,可以采用快排或者堆,其思想都是选取当前最小的两个堆进行合并。复杂度均为O(nlogn),如果用有序队列维护,时间复杂度为O(n)。
每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆,所以需要维护两个单调递增队列,一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。
每次选择时有三种状态:
1.选取队一的队首两个
2.选取队2的的队首两个
3.选取二者队首各一个
只需对每个队列的指针做相应的更改。
特别注意初始化。
这道题很好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说,一旦有新元素加入其中,则新元素一定大于原有元素。(很显然,由于队列1的单调性)。
也就是说,队列的单调性是自然而然的。是不需要维护的。要善于观察分析,才能发现。
X. 
这个题目大家很熟悉的方法是进行排序后用堆进行操作,实现哈夫曼树,两步操作复杂度为O(nlogn)。自然这里的排序和上面一样,都是将数据映射到数轴上,再进行贪心处理。不过我在这里要进行一下扩展,利用一下单调性。原算法中堆的每次操作级别为logn,原因是每次的操作破坏了数据的单调性。如果我们把每次新合并出来的数据另外处理,保持原来数据的单调性,就减少了操作,降低了复杂度。具体方法是利用队列的性质,开两个数组,第一个数组存排序后的原数据,另一个数组存每次合成的数据,因为原数据是从小到大排布,新数组中的数据也会由小到大排布,这样,每次只要在这两个队列的开头选两次最小的元素再进行合并,并将生成的数据放入第二个队列的末端就可以了。这样的处理方式将合并过程的复杂度变成了O(n)

    对于此类的如果生成的数,破坏了原来的单调性,则可以另外以开队列的方式解决,就是单调队列的问题
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        int ans=0;
        scanf("%d",&n);
        FOR(i,1,n)scanf("%d",&a[i]);
        int al=1,ar=n,bl=1,br=0;
        sort(a+1,a+1+n);
        FOR(i,2,n)
        {
            int ele=0;
            if(bl>br||al<=ar&&a[al]<b[bl])
                ele+=a[al++];
            else if(bl<=br)
                ele+=b[bl++];
            if(bl>br||al<=ar&&a[al]<b[bl])
                ele+=a[al++];
            else if(bl<=br)
                ele+=b[bl++];
            ans+=(b[++br]=ele);
        }
        printf("%d\n",ans);
    }
    return 0;
}
  其实这题做法有很多。可以O(n^3)动规,可以O(n^2*logn)贪心,也可以用堆优化使得贪心的复杂度降到O(n*logn)。
  但其实还有一种O(n)的合并方法——单调队列。
  我们维护两个队列q1,q2,其中q1表示原本的那些堆(初始按升序排序),每次新合成的果子加入q2队尾。因为新合成的堆是越来越大的,所以q2也是有序的。那么我们很容易就可以从q1和q2中选出最小的两堆进行合并。
  但是一开始题目给你的序列显然不是有序的对吧。如果用快排的话总复杂度就是O(n*logn+n),还不如堆优化。那么我们考虑桶排序,就可以把复杂度降为O(n+S)。

inline int read() {
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-'0' ;
    return k*f ;
}
int n ; int num[M+10], hh[N] ;
deque<int>q1 ;
deque<int>q2 ;

int main() {
    n = read() ;
    int x, y, ans = 0, mx = -INF ;
    memset(num,0,sizeof(num)) ;
    for(int i=1;i<=n;i++) {
        x = read() ;
         num[x]++ ;
         mx = mx > x ? mx : x ;
    }
    for(int i=1;i<=mx;i++) {
        while(num[i]) {
            q1.push_back(i) ; num[i] -- ;
        }
    }
    for(int i=1;i<n;i++) {
        if(!q2.size()) x = q1.front(), q1.pop_front() ;
        else if(!q1.size()) x = q2.front(), q2.pop_front() ;
        else {
            if(q1.front() < q2.front()) {
                x = q1.front(), q1.pop_front() ;
            } else x = q2.front(), q2.pop_front() ;
        }
        if(!q2.size()) y = q1.front(), q1.pop_front() ;
        else if(!q1.size()) y = q2.front(), q2.pop_front() ;
        else {
            if(q1.front() < q2.front()) {
                y = q1.front(), q1.pop_front() ;
            } else y = q2.front(), q2.pop_front() ;
        }
        ans += (x+y) ;
        q2.push_back(x+y) ;
    }
    printf("%d",ans) ;
    return 0 ;
}

从这个问题可以深挖出神奇的哈夫曼树问题。
因为这题里合并的是二叉树,所以结点数量什么的都不用考虑。用堆维护数据,每次贪心取两个最小的合并即可(因为要使大数被累加的次数尽量少)
10 priority_queue <long long,vector<long long>,greater<long long> >q;
11 LL ans;
12 int n;
13 int main(){
14     scanf("%d",&n);
15     int i,j;
16     LL x;
17     for(i=1;i<=n;i++){
18         scanf("%lld",&x);
19         q.push(x);
20     }
21     for(i=1;i<n;i++){
22         LL tmp=q.top();q.pop();
23         tmp+=q.top();q.pop();
24         q.push(tmp);
25         ans+=tmp;
26     }
27     printf("%lld\n",ans);
28     return 0;
29 }


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