石子合并问题

石子合并问题它有三种分类。。。

1、给出N堆石子,要将N堆石子合并成一堆,规定:每次任意合并两堆石子,花费为合并后的石子个数,求合并为一堆石子的最多或最少花费。

这是最简单的情况,直接贪心,每次取最小的两堆石子合并,就能得到答案。类似oj上的23940。

2、给出N堆石子线性排成一列,要将N堆石子合并成一堆,规定:每次合并相邻两堆石子,花费为合并后的石子个数,求合并为一堆石子的最多或最少花费。

这种就比较复杂点,如果你看过矩阵连乘,你会发觉很相似。所以我们类似地用矩阵连乘的递推方式,就可以得到它的状态转移方程:设dp[ i ] [ j ]为第 i 堆石子到 j 堆石子合并的最优值,sum[ i ][ j ]为第 i 堆石子到第 j 堆石子的总和。

0 (i = j)

即 dp[ i ][ j ]={ min ( dp[ i ][ k ] +dp[ k+1 ][ j ]+sum[ i ][ j ] ) (i != j)

其代码实现


3、第三种就是在第二种的基础上,将线性改成环形排列。。。。
其思想分析跟第二种是差不多的,但他是环形的,就意味他不只是1 2 3 4 这样合并,还可以4 1 2 3这样取。不难发现,其实解法类似。不过由于是环形,我们要在每次相加只有都对个数取余数,以防止数组越界。

0 j==0;

dp[ i ] [ j ]={​min( dp[ i] [ k ]+dp[ (k + i+1)% n][ j - k- 1]+sum[ i ][ j ] ) j 〉0

​ 重点记住:不过,此处的j与前面(2)中的j意义并不一样,此处的j意义为:从第i堆出发,往下数j堆石子。而且sum的取法有些不一样,例如,总共5堆石子,从第四堆后取4堆石子,就分为两部分去取:4-5,1-2 去取。但要记住sum和dp都一样,第一维记录的是起点,第二维代表的是从起点后取合并多少堆石子。

代码如下:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAX 205
int dp1[MAX][MAX];
int dp2[MAX][MAX];
int p[MAX];
int n;
using namespace std;
int getsum(int i,int j){//求第 i 堆起,取j 堆的sum。
     if(i+j<n){
      int k;
       if(i==0)
        k=0;
     else
        k=p[i-1];
     return p[i+j]-k;
     }
     else
        return getsum(0,i+j-n)+getsum(i,n-i-1);//分两部份。
}
void itin(){//初始化,最小子问题。
   for(int i=0;i<n;i++){
    dp1[i][0]=0;
    dp2[i][0]=0;
   }
}
int main(void){
   scanf("%d",&n);
             itin();
        int x;
        for(int i=0;i<n;i++){//前i堆和。
            scanf("%d",&x);
            if(i==0)
              p[i]=x;
            else
                p[i]=p[i-1]+x;
        }
        for(int i=0;i<n;i++){
            dp1[i][i]=0;
            dp2[i][i]=0;
        }
        for(int j=1;j<n;j++)//枚举长度。
          for(int i=0;i<n;i++){
            int temp=getsum(i,j);
       //  printf("%dok
",temp);
                dp1[i][j]=INF;
                dp2[i][j]=-1;
            for(int k=0;k<j;k++){
                dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[(i+k+1)%n][j-k-1]+temp);//递推取最优。
                dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[(i+k+1)%n][j-k-1]+temp);
            }
        }
        int mi=dp1[0][n-1];
        int ma=dp2[0][n-1];
   for(int i=0;i<n;i++){//枚举起点找最优。
        mi=min(dp1[i][n-1],mi);
        ma=max(dp2[i][n-1],ma);
   }
   printf("%d %d
",mi,ma);
}

  

原文地址:https://www.cnblogs.com/linhaitai/p/9127621.html