石子合并问题
Posted 海苔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了石子合并问题相关的知识,希望对你有一定的参考价值。
石子合并问题它有三种分类。。。
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\n",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\n",mi,ma); }
以上是关于石子合并问题的主要内容,如果未能解决你的问题,请参考以下文章