石子合并问题 C语言
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了石子合并问题 C语言相关的知识,希望对你有一定的参考价值。
合并石子问题:圆形操场,四周n堆石子,每堆石子数不一。现将石子合并为一堆,每次仅可选取相邻两堆合并为一堆,新堆石子数为该次合并的得分。设计合并为一堆的最小(或者最大)得分算法。
需要程序。。。。
1. 石子合并
在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,计算出N堆石子合并成一堆的最小得分和最大得分。
;
输入数据:
第一行为石子堆数n;
第二行为每堆的石子数,每两个数之间用一个空格分隔。
输出数据:
第一行中的数是最小得分,第二行是最大得分
#include<stdlib.h>
void main()
int n,i,j,k,p,sum=0,m,max=0,min,*a,**f,t;
printf("请输入石子的堆数:");
scanf("%d",&n);;
a=new int[2*n];//动态数组存储单元
a[0]=0;
for(i=1;i<=n;i++)
printf("请输入中第%d堆石子的个数:",i);
scanf("%d",&a[i]);//输入石子各堆数的个数
for(t=1;t<n;t++)
a[n+t]=a[t];
//形成一个看似循环的数组
for(t=1;t<2*n;t++)
f=new int *[n+1];//为动态二维数组分配内存单元
for(i=0;i<=n;i++)
f[i]=new int [n+1];
for(i=0;i<=n;i++)//为动态二维数组初始化为0
for(j=0;j<=n;j++)
f[i][j]=0;
for(i=1;i<=n;i++)//数组第一列赋值为0(经分析得)
f[i][1]=0;
for(j=2;j<=n;j++)//为数组其他元素赋值
for(i=1;i<=n;i++)
max=0;
for(k=1;k<=j-1;k++)
m=f[i][k]+f[(i+k-1)%n+1][j-k];
if(max<m) max=m;
sum=0;
for(p=i;p<=i+j-1;p++)//最终每堆石子都将有两堆石子合并
sum=sum+a[p];
f[i][j]=sum+max;
max=f[1][n];//对最后一列寻找最大值即为最大得分
for(i=1;i<=n;i++)
if(max<f[i][n]) max=f[i][n];
printf("最大得分为:%d\n",max);
min=99999999;
for(i=1;i<=n;i++)
if(f[i][n]<min)
min=f[i][n];
printf("最小得分为:%d\n",min);
system("PAUSE");
写了好半天。。。好像还是有点问题,你自己再改改,最大分数是对的
石子合并问题
石子合并问题它有三种分类。。。
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); }
以上是关于石子合并问题 C语言的主要内容,如果未能解决你的问题,请参考以下文章