拔河分组

有12个同学要分成两个组进行拔河比赛,为使比赛公平,分组时要求每组6个同学,且两组的体重之和相等;

已知这12个同学的体重分别为:38、39、47、35、46、58、51、42、36、40、59、39;

根据他们的体重(为方便计算以全部转化为整数,单位为kg)实施分组,若无法实现数据个数与数据和均相等的分组,标注为“无法均分”;


基本方法- -双均分法

1.说明:

我们把“要求每组数据个数相等数据和也相等”的分组称为双均分,最简单的双均分问题是把已知的4个数b1、b2、b3、b4分成两个组,问题的判断比较简单,把4个数排序,设b1< b2< b3< b4,只要判断b1+b4=b2+b3是否成立即可;

当涉及双均分的数据较多时,分组就变得比较复杂了;

从键盘输入(或随机产生)的12个正整数存储在b数组中,求出总和s,若和s为奇数,显然无法分成重量相等的两组,提示后退出;若s为偶数,则s1=s/2;

为方便调整,设置数组a存储b数组的下标值,即a(i):1~12;

考察b(1)所在的组,只要另从b(2)~b(12)中选取5个数,即定下a(1)=1,其余的a(i)(i=2,……,6)在2~12中取不重复的数,因为组合与顺序无关,不妨设:

  • 2<=a(2)< a(3)<……< a(6)<=12

从a(2)取2开始,以后a(i)从a(i-1)+1开始递增1取值,对a(2)~a(6)设置5重循环,这样可避免重复又不至于遗漏;

在内循环中,计算s=b(1)+b(a(2))+……+b(a(6)),若s=s1,满足要求,实现平分;

对输入的12个整数并不总有解,有解时,找到并输出所有的解,没有解时,显示相关提示信息“无法实现平分”;

2.程序设计:

#include<stdio.h>
#include<math.h>
#include<time.h>
int main()
{
   int j,k,m,a[7],b[13];
   long t,s1,s=0;
   t=time(0)%1000;
   srand(t);        /*随机数发生器初始化*/
   printf("已知12个同学的体重分别为:\n");
   for(s=0,k=1;k<=12;k++)      /*输入12个整数*/
   {
      s+=b[k]=rand()%25+35;
      printf("%d",b[k]);
   }
   if(s%2==0)
   {
      printf("\n以上12个整数总和为%d \n",s);
      s1=s/2;
   }
   else
   {
      printf("和为奇数,无法平分!\n");
   }
   a[1]=1;
   m=0;
   for(a[2]=2;a[2]<=8;a[2]++)
    for(a[3]=a[2]+1;a[3]<=9;a[3]++)
     for(a[4]=a[3]+1;a[4]<=10;a[4]++)
      for(a[5]=a[4]+1;a[5]<=11;a[5]++)
       for(a[6]=a[5]+1;a[6]<=12;a[6]++)
       {
          for(s=0,k=1;k<=6;k++)
             s=s+b[a[k]];
          if(s==s1)           /*满足均分条件时输出*/
          {
             m++;
             printf("NO%d:",m);
             for(j=1;j<=6;j++)
                printf("%d",b[a[j]]);
             printf("\n");
          }
       }
       if(m>0)
          printf("共有以上%d种分发\n",m);
       else
          printf("无法实现二维均分\n");
    }

3.程序运行示例:

已知12个同学的体重分别为:
38 39 47 35 46 58 51 42 36 40 59 39
以上12个整数总和为530
NO 1:38 39 47 46 36 59
NO 2:38 39 47 42 40 59
......
NO 15:38 51 42 36 59 39
共有以上种分发

  • 双均法拓广

一般地,对已知的2n(n从键盘输入)个正整数,试把这些数分为2组,每组n个数,且每组数据的和相等或两组数据和相差最小;

这里把分2组的数据个数一般化为2n个,每组n个数据要求不变,若能分成每组数据和相等,则输出所有不同的分发;若不能分成每组数据和相等,则求出两组数据和相差最小的分组;

1.说明:

求解拓广的双均分问题要求更高了,可采用回溯法逐步实施调整;

1)、回溯实施

  • 对于已有的存储在b数组中的2n个正整数(随机产生或键盘输入均可),求出总和s及其和的一半s1(若这2n个数的和s为奇数,则s1=s/2非整数);

  • 把这2n个数分成2组,每组n个数,为方便调整,设置数组a存储b数组的下标值,即a(i):1~2n

  • 考察b(1)所在的组,只要另从b(2)~b(2n)中选取n-1个数,即定下a(1)=1,其余的a(i)(i=2,……,n)在2~2n中取不重复的数,因为组合与顺序无关,不妨设: 2<=a(2)< a(3)<……< a(n)<=2n ;

  • 从a(2)取2开始,以后a(i)从a(i-1)+1开始递增1取值,直至n+i为止,这样可避免重复;

2)、双均分判断

  • 若s2!=s1,则a(n)继续增1再试,如果a(n)已增至2n,则回溯前一个a(n-1)增1再试,如果a(n-1)已增至2n-1,继续回溯,直至a(2)增至n+2时,结束;

3)、无法双均分处理

  • 双均分问题并不总能实现,例如当总和s为奇数时显然无法双均分,就是s为偶数,也不一定能实现双均分;

  • 对于不能实现双均分的情形,同样应用回溯探求“两组数据和相差最小”的分组:当a(n)已取值时,计算s2=b(1)+b(a(2))+……b(a(n)),d=|s2-s1|;在d与min比较中求取d的最小值,并用s3记录最小时的n个数据和,用c数组记录此时的下标数组a的值

  • 回溯完成后,输出两组数据和相差最小2*min,并据c数组输出分组的一组数据;

2.程序设计:

#define N 50
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
int main()
{
   int n,m,a[N],c[N],b[2*N],i,j,k,t;
   long s2,s3,s=0;
   double d,s1,min=1000;
   t=time(0)%1000;
   srand(t);          /*随机数发生器初始化*/
   printf("请输入n:");
   scanf("%d",&n);
   printf("已知%d个同学的体重分别为:\n",2*n);
   for(s=0,i=1;i<=2*n;i++)       /*产生2n个随机整数*/
   {
      s+=b[i]+rand()%25+30;      /*s为2n个整数之和*/
      printf("%d",b[i]);
   }
   printf("\n以上%d个重量总和为%d\n",2*n,s);
   s1=(double)s/2;               /*s2为探索过程中n个数据之和*/
   i=1;
   a[1]=1;
   m=0;
   while(s%2==0)
   {
      if(i==n)
      {
         for(s2=0,j=1;j<=n;j++)     /*s2为探索过程中n个数据之和*/
            s2+=b[a[j]];
         if(s1==(double)s2)         /*满足均分条件时输出*/
         {
            m++;
            if(m<=3)
            {
               printf("NO%d:",m);
               for(j=1;j<=n;j++)
                  printf("%d ",b[a[j]]);
               printf("\n");
            }
         }
      }
      else
      {
         i++;
         a[i]=a[i-1]+1;
         continue;
      }
      while(a[i]==n+1)
         i--;            /*调整或回溯*/
      if(i>1)
         a[i]++;
      else
         break;
   }
   if(m>0)
   {
      printf("\n共有以上%d种分法\n",m);
      return;
   }
   else
   {
      printf("无法实现二组重量均分!\n");
      i=1;
      a[1]=1;
      m=0;
      while(1)
      {
         if(i==n)
         {
            for(s2=0,j=1;j<=n;j++)
               s2+=b[a[j]];
            d=fabs((double)s2-s1);
            if(d<min)          /*d与min比较求取最小值*/
            {
               min=d;
               s3=s2;
               for(k=1;k<=n;k++)
                  c[k]=a[k];
            }
         }
         else
         {
            i++;
            a[i]=a[i-1]+1;
            continue;
         }
         while(a[i]==n+i)
            i--;           /*调整或回溯*/
         if(i>1)
            a[i]++;
         else
            break;
      }
      printf("用以下分组可使得两组重量相差最小为%.0f:\n",2*min);
      for(j=1;j<=n;j++)
         printf("%d",b[c[j]]);
      printf("\n该组重量为%d;余下为第2组,重量为%ld\n",s3,s-s3);
   }
}

3.程序运行示例及其注意事项:

请输入n:10
已知20个同学的体重分别为:
31 39 54 30 42 49 44 35 41 36 49 33 48 32 49 49 39 48 43 38
以上20个重量总和为829
无法实现二祖重量均分!
用以下分组可使得两组重量相差最小为1:
31 39 54 30 42 49 44 35 41 49
该组重量为414;余下为第2组,重量为415

注意:

  • 以上程序设计对两组均分只输出其中一个组,另一组省略输出,即为其余数组成;

  • 如果在输出中出现有些解“重复”,这是由于2n个数据有重复(例如不同同学有相同的体重)造成的;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值