P1880 [NOI1995]石子合并


https://www.luogu.org/problemnew/show/P1880
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分
https://www.lucien.ink/archives/142/
https://www.cnblogs.com/ehznehc/p/9801249.html
一、贪心
贪心只能处理“任取两堆”,而不能处理“相邻两堆”。任取两堆的题目就是 合并果子。
二、划分阶段
本题是区间DP的模板题(然而对蒟蒻还是很难)
很容易想到用f[i][j]表示i~j的最大(小)得分。
三、状态转移
在i~j这一区间内,枚举下标k进行拆分。拆分区间是区间DP的重要思想之一。
合并i~k与k+1~j的区间,会加上i~j之间所有石子个数之和。
故我们得到状态转移方程为f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]) (最小值max改为min)
其中s为前缀和(不懂的自觉退役……自觉补习)。
四、环
dalao们可能会使用高级数据结构,但其实有一种更简单的方法:把原数组往后复制一遍。
以样例4 5 9 4为例,复制后得到:
4 5 9 4 4 5 9 4
i~j长度不能超过n
基础DP的代码比较简短,但麻烦的地方有很多,关键在于环。
for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=2*n;j++)
        {
            if(j-i>=n)
                break;
            for(int k=i;k<j;k++)
            {
                ma[i][j]=max(ma[i][j],ma[i][k]+ma[k+1][j]+sum[j]-sum[i-1]);
                mi[i][j]=min(mi[i][j],mi[i][k]+mi[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
这是之前的一份错误代码,它使用直接计算长度的方式来保证长度<=n。
但是这样的转移顺序会出现问题。如,外层循环i=1,第二层j=10,内层循环k=4时:
f[1][10]=max(f[1][10],f[1][4]+f[5][10]+s[10]-s[0])
可以发现f[5][10]还未转移,这也是玄学40分的原因。

解决方法倒是有不少,这里 借 鉴 了其他题解,用r=i+j-1来记录右边界,使r和i,j直接联系,完美避免了上述情况。
详情见代码。
七、代码
码风丑陋勿喷,没有优化勿喷。
另外,以此题的数据范围完全用不到四边形不等式这类的高档算法。
    cin>>n;
    sum[0]=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        a[i+n]=a[i];
    //输入
    }
    for(int i=1;i<2*n;i++)
    {
        sum[i]=sum[i-1]+a[i];
        mi[i][i]=ma[i][i]=0;
        //维护前缀和,再次初始化数组
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=1;i+j-1<2*n;j++)
        {
            r=i+j-1;
            for(int k=j;k<r;k++)
            {
                ma[j][r]=max(ma[j][r],ma[j][k]+ma[k+1][r]+sum[r]-sum[j-1]);
                mi[j][r]=min(mi[j][r],mi[j][k]+mi[k+1][r]+sum[r]-sum[j-1]);//状态转移
            }
        }
    }
    ansmin=0x7fffffff;
    ansmax=-1;
    for(int i=1;i<=n;i++)
    {
        ansmin=min(ansmin,mi[i][i+n-1]);
        ansmax=max(ansmax,ma[i][i+n-1]);
    //选择最大(小)值
    }
https://blog.csdn.net/SunPeishuai/article/details/81772406
首先,要计算合并的最大值、最小值,既然是动态规划,我们需要洞悉其中一些关联且确定的状态。

以下以最大值为例。

既然是最大值,那么求得的结果是否满足每一区间都是该区间所能达得到的的最大值?

显然是这样的。反证法:倘若有一个区间不是,那么换做该区间取得最大值的方案,最终结果将比原得分大。显然必定满足任意区间得分一定是该区间内的最大值。

这样我们可以定义状态f[i][j],表示i到j合并后的最大得分。其中1<=i<=j<=N。

既然这样,我们就需要将这一圈石子分割。很显然,我们需要枚举一个k,来作为这一圈石子的分割线。

这样我们就能得到状态转移方程:

f[i][j] = max(f[i][k] + f[k+1][j] + d(i,j));其中,1<=i<=<=k<j<=N。

d(i,j)表示从i到j石子个数的和。

那么如何编写更快的递推来解决这个问题?

在考虑如何递推时,通常考虑如下几个方面:

是否能覆盖全部状态?

求解后面状态时是否保证前面状态已经确定?

是否修改了已经确定的状态?

也就是说,在考虑递推顺序时,务必参考动态规划的适应对象多具有的性质,具体参考《算法导论》相关或百度百科或wiki。

既然之前说过我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态。

如,i=1 to 10,j=1 to 10,k=1 to 9.当i=1,j=5,k=3时,显然状态f[k+1][j]没有结果。

那么,我们是不是应该考虑枚举k?

但这样i和j就难以确定了。

我们不难得到一个两全的方法:枚举j-i,并在j-i中枚举k。这样,就能保证地推的正确。



    for(int i=1;i<=n+n;i++) 
    { 
        scanf("%d",&num[i]); 
        num[i+n]=num[i]; 
        s[i]=s[i-1]+num[i]; 
    } 
    for(int p=1;p<n;p++) 
    { 
        for(int i=1,j=i+p;(j<n+n) && (i<n+n);i++,j=i+p) 
        { 
            f2[i][j]=999999999; 
            for(int k=i;k<j;k++) 
            { 
                f1[i][j] = max(f1[i][j], f1[i][k]+f1[k+1][j]+d(i,j)); 
                f2[i][j] = min(f2[i][j], f2[i][k]+f2[k+1][j]+d(i,j)); 
            } 
        } 
    } 
    minl=999999999; 
    for(int i=1;i<=n;i++) 
    { 
        maxl=max(maxl,f1[i][i+n-1]); 
        minl=min(minl,f2[i][i+n-1]); 
    }



    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        w[i+n]=w[i];
    }
    for(int i=1;i<=2*n;i++)
    {
        sum[i]=sum[i-1]+w[i];
    }
    for(int len=2;len<=n;len++)///区间长度
    for(int i=1;i<=n*2-len;i++)///这里要注意
    {             
       int j=i+len-1;
       dpmin[i][j]=1e9;
       for(int k=i;k<j;k++)///区间内的任意位置
       {
           dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+sum[j]-sum[i-1]);
           dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+sum[j]-sum[i-1]);
       }
    }
    int maxx=0;
    int minn=100000000;
    for(int i=1;i<=n;i++)
    {
        minn=min(minn,dpmin[i][i+n-1]);
        maxx=max(maxx,dpmax[i][i+n-1]);
    }







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