拔河分组
2016-12-31 14:02
375 查看
有12个同学要分成两个组进行拔河比赛,为使比赛公平,分组时要求每组6个同学,且两组的体重之和相等;
已知这12个同学的体重分别为:38、39、47、35、46、58、51、42、36、40、59、39;
根据他们的体重(为方便计算以全部转化为整数,单位为kg)实施分组,若无法实现数据个数与数据和均相等的分组,标注为“无法均分”;
我们把“要求每组数据个数相等数据和也相等”的分组称为双均分,最简单的双均分问题是把已知的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.程序设计:
3.程序运行示例:
双均法拓广
一般地,对已知的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.程序设计:
3.程序运行示例及其注意事项:
注意:
以上程序设计对两组均分只输出其中一个组,另一组省略输出,即为其余数组成;
如果在输出中出现有些解“重复”,这是由于2n个数据有重复(例如不同同学有相同的体重)造成的;
已知这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 ,c ,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; 4000 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个数据有重复(例如不同同学有相同的体重)造成的;