石子合并问题C语言求优秀算法

Posted

tags:

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

这个问题问问上曾有人问过,问的是PASCAL语言的。其实语言无所谓,在下在这里试求有没有更简单的算法。问题:你有一堆石头质量分别为W1,W2,W3...WN.(W<=100000)现在需要你将石头合并为两堆,使两堆质量的差的绝对值为最小。 输入:第一行为整数N(1<=N<=20),表示有N堆石子。第二行:是n个数,为每堆石子的质量。 输出:一行。只需输出合并后两堆的质量差的绝对值的最小值样例:输入:55 8 13 27 14输出:3 我的算法:我想的是一个笨办法,我把该题转化成另一个问题就是:给你一些数,在他们中间穷举取遍+、-号,进行运算最后得出的数取绝对值,求最小绝对值。根据这个思路,我将用于存放石头质量的数组a[20]每个元素都赋初值0,在进行输入石头质量。如上题输入后数组就变为5 8 13 27 14 0 0 0 0……然后用19个循环来穷举中间的+、-号,最后得出的绝对值如果比以前的小,则替换,否则不替换,最终得出的是最小绝对值。但19个循环太麻烦了啊,程序显得冗长。我想问有没有更优秀的算法??谢谢各位了。

先将石头求和再除2得m,求<=m得那一堆石头,再用背包法(动态规划思想,高效)或直接回朔搜索(20较小,也会轻松搞定)。
一般动态规划相比搜索写代码要简单多,但背包那问题涉及到内存的申请和释放等指针问题就有点列外啦,呵呵,小弟也没写,简单指导下。抛砖引玉。。。。。
参考技术A 看我的,用逻辑尺方法
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
main()

int *w;/* 石子的质量数组 */
int *a; /* 逻辑尺。形如101100... 0表示在第一组,1表示在第二组 */
int n;/* 石子堆数 */
long pn; /* pn=2^n-1 排列组合的次数 */
long p; /* pn的临时变量,即排列组合的对应的十进制数 */
int i; /*循环变量*/
int sum1,sum2; /* 两堆石子的质量之和*/
int sub=0; /*两堆石子的质量差*/
int PN; /* sub最小时的排列组合之数*/

/*读入数据*/
printf("n="); scanf("%d",&n);

/*动态分配内存,生成相应大小的数组*/
w=(int *)malloc(n*sizeof(int));
a=(int *)malloc(n*sizeof(int));

/*读入石子质量,并设最小质量的差为全部石子的质量之和,即设一个最大的值 */
for(i=0;i<n;i++)

scanf("%d",&w[i]);
sub+=w[i];


/*计算排列组合次数*/
pn=(int)pow(2,n)-1;

while(pn>0)

/*生成pn排列组合数对应的逻辑尺*/
p=pn;
for(i=0;i<n;i++)
a[i]=p%2;
p=p/2;

/*按逻辑尺计算两堆的质量*/
sum1=sum2=0;
for(i=0;i<n;i++)

if(a[i]==0)sum1+=w[i];
else sum2+=w[i];

/*确定当前最小的质量差,并记录组合数于PN中*/
if(abs(sum1-sum2)<sub)
sub=abs(sum1-sum2);
PN=pn;

/*下一个组合数*/
pn--;

/*输出结果*/
/*如何分堆*/
p=PN;
for(i=0;i<n;i++)
a[i]=p%2;
p=p/2;

printf("第一堆:");
for(i=0;i<n;i++)
if(a[i]==0)printf("%d ",w[i]);
printf("\n第二堆:");
for(i=0;i<n;i++)
if(a[i]==1)printf("%d ",w[i]);

printf("\n质量差:%d\n",sub);
参考技术B 我觉得你的方法类似于枚举。用2进制划分比较明确。
从题给的数据量来看,使用枚举时间的复杂度是够的2^19
可以考虑使用动态规划,但是时间复杂度不会太大改善。
参考技术C 我想的一个方法,这样做:先求出总重量,再求出分成两堆的重量m,然后用剩下的石子来凑m的重量,找到一个最接近于m的方法,应该就可可以了吧。
先从小往大加,如果超了的话,就停止加大的,然后扔小的。。。这样一直找到最接近的。。

算法提高 合并石子(DP)

问题描述
  在一条直线上有n堆石子,每堆有一定的数量,每次可以将两堆相邻的石子合并,合并后放在两堆的中间位置,合并的费用为两堆石子的总数。求把所有石子合并成一堆的最小花费。
输入格式
  输入第一行包含一个整数n,表示石子的堆数。
  接下来一行,包含n个整数,按顺序给出每堆石子的大小 。
输出格式
  输出一个整数,表示合并的最小花费。
样例输入
5
1 2 3 4 5
样例输出
33
数据规模和约定
  1<=n<=1000, 每堆石子至少1颗,最多10000颗。
 

题解

技术分享图片
#include<stdio.h>
#include<stdlib.h>
#define INIF 0x3f3f3f3f
int N;
int dp[1001][1001] = {0};
int sum[1001][1001];
int num[1001];
void minsz()
{
    int i, j, k, t, len, minx;
    for(i = 1; i <= N; i++)
    {
        sum[i][i] = num[i];
        for(j = i + 1; j <= N; j++)
        {
            sum[i][j] = sum[i][j-1] + num[j];//计算堆到堆的距离
        }
    }
    for(j = 2; j <= N; j++)
    {
        for(i = j-1; i > 0; i--)
        {
            minx = INIF;
            dp[i][j] = INIF;
            for(k = i; k < j; k++)
            {
                t = dp[i][k] + dp[k+1][j] + sum[i][j];
                if(t < minx)
                    minx = t;
            }
            dp[i][j] = minx;
        }
    }
    printf("%d\n", dp[1][N]);
}

int main()
{
    int i;
    scanf("%d", &N);
    for(i = 1; i <= N; i++)
    {
        scanf("%d", &num[i]);
    }
    minsz();
    return 0;
}
View Code

 

以上是关于石子合并问题C语言求优秀算法的主要内容,如果未能解决你的问题,请参考以下文章

石子合并问题

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

求指教,以下皆用数组在c语言里编写程序

c语言求1-1000素数的算法问题

2018年全国多校算法寒假训练营练习比赛(第三场) 小牛再战(博弈)

求 算法:C语言实现 的PDF