石子合并问题 C语言

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了石子合并问题 C语言相关的知识,希望对你有一定的参考价值。

合并石子问题:圆形操场,四周n堆石子,每堆石子数不一。现将石子合并为一堆,每次仅可选取相邻两堆合并为一堆,新堆石子数为该次合并的得分。设计合并为一堆的最小(或者最大)得分算法。
需要程序。。。。
1. 石子合并

在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,计算出N堆石子合并成一堆的最小得分和最大得分。



输入数据:

第一行为石子堆数n;

第二行为每堆的石子数,每两个数之间用一个空格分隔。

输出数据:

第一行中的数是最小得分,第二行是最大得分

参考技术A #include<stdio.h>
#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语言的主要内容,如果未能解决你的问题,请参考以下文章

石子合并问题

石子合并问题

c语言基础问题 如何合并字符

c语言有序链表合并,我找不到问题所在,求纠错!!

Wannafly挑战赛24-A-石子游戏--思维题

C语言合并两个数组