i=1;a[i]=0; k=1; while(1) {t=1;
for(j=1;j
if(a[j] && a[j]==a[i]) {t=0;break;} // 非零元素相同,则返回 if(t && k==n-m && i==n) // 已取n 个值且0的个数为n-m时输出解 {s++;
for(j=1;j<=n;j++) printf(\ printf(\
if(s==0) printf(\ }
    if(t && (k       if(k       else a[i]=1;               // 若0的个数已达到n-m,则不再取0了         continue;       }      while(a[i]==n) i--;           // 调整或回溯或终止       if(i>0)        {if(a[i]==0) k--;   // 改变取值为0的元素值前先把0的个数k减1          a[i]++;       }      else break;   }   printf(\}   5-10  8对夫妇特殊的拍照  一对夫妇邀请了7对夫妇朋友来家餐聚,东道主夫妇编为0号,其他各对按先后分别编为1,2,…,7号。  餐聚后拍照,摄影师要求这8对夫妇男左女右站在一排,东道主夫妇相邻排位在横排的正中央,其他各对排位,1号夫妇中间安排1个人,2号夫妇中间安排2个人,依此类推。  共有多少种拍照排队方式? 1.  设计要点  在n组每组2个相同元素(相当于n对情侣),a数组从0取到2n-1不重复,对n同余的两个数为一对编号:余数为0的为0号(即东道主),余数为1的为1号,…,余数为n-1的为n-1号。  例如,n=4,数组元素为0与4,对4同余,为一对“0”; 1与5对4同余,为一对“1”;一般地, i与4+i对4同余,为一对i,(i=0,1,2,3)。      返回条件修改为(当j a(j)=a(i) or a(j)%n=a(i)%n and (a(j)>a(i) or a(j)+1!=i-j) 其中a(j)=a(i),为使a数组的2n个元素不重复取值;  a(j)%n=a(i)%n and a(j)>a(i),避免同一对取余相同的数左边大于右边,导致重复; a(j)%n=a(i)%n and a(j)+1!=i-j,避免同一对数位置相差不满足题意相间要求。 例如,a(j)=0时,此时a(i)=n,为0号情侣,位置应相差1(即中间没有人),即i-j=1。 a(j)=1时,此时a(i)=n+1,为1号情侣,位置应相差2(即中间有1人),即i-j=2。 这些都应满足条件a(j)+1=i-j。如果a(j)+1!=i-j,不满足要求,返回。  设m=2n,若满足条件(g>0 and i=m and a(1)%n 2. 程序实现  // 8对夫妇拍照  #include  {int  i,j,g,n,m,s,a[20];   printf(\scanf(\ m=2*n;   i=1;a[i]=0;s=0;  while(1)    {g=1;      for(j=1;j       if(a[j]==a[i] || a[j]%n==a[i]%n && (a[j]>a[i] || a[j]+1!=i-j))         {g=0;break;}   // 出现相同元素或同余小在后时返回             if(g && i==m && a[1]%n for(j=1;j<=m;j++)                   printf(\输出一个排列                printf(\            }           }        if(g && i {i++;a[i]=0;continue;}        while(a[i]==m-1) i--;             // 回溯到前一个元素         if(i>0) a[i]++;       else break;          }   printf(\共有解s=%d个。\\n\}  习题6    6-1  n个矩阵连乘问题   设矩阵A为p行q列,矩阵B为q行r列,求矩阵乘积AB共需做pqr次乘法。 试求n(n>2)个矩阵Mi(i?1,2,?,n)的乘积M1M2?Mn的最少乘法次数。其中n与Mi的行、列数ri,ri?1均从键盘输入。  解:注意ri?1是Mi的列数,也是Mi?1的行数,这样才能确保Mi与Mi?1能相乘。 多个矩阵相乘,满足乘运算结合律。  例如,求M1M2M3,先求前两个矩阵的乘积(M1M2)M3,还是先求后两个的乘积M1(M2M3),都是可以的,但两者的乘法次数不一定相等,我们要求最少乘法次数。  设m(i,j)是求乘积MiMi?12?Mj的最少乘法次数,则有递推关系     m(i,i?1)?r(i)r(i?1)r(i?2)    (i+1=j)  m(i,j)?min(m(i,k)?m(k?1,j)?r(i)r(k?1)r(j?1)) (i≤k≤j,i 初始(边界)条件:m(i,j)=0  (i=j) 最优值为m(1,n).   程序设计:    为递推方便,设置d=i-j。显然,1≤d≤n-1。 // 矩阵连乘    #include  {int d,n,i,j,k,t,r[100],m[100][100];   printf(\请输入矩阵的个数 n :\  printf(\请输入第1个矩阵的行数 :\ for(i=1;i<=n-1;i++)    {printf(\请输入第%d个矩阵的列数,也是第%d个矩阵的行数 :\   scanf(\     }   printf(\请输入第%d个矩阵的列数 :\ for(i=1;i<=n;i++)    m[i][i]=0;     for(d=1;d<=n-1;d++)  for(i=1;i<=n-d+1;i++) {j=i+d;     m[i][j]=m[i][i]+m[i+1][j]+r[i]*r[i+1]*r[j+1]; for(k=i+1;k {t=m[i][k]+m[k+1][j]+r[i]*r[k+1]*r[j+1]; if(t printf(\个矩阵连乘的乘法次数的最小值为:%d \\n\}   6-2  应用顺推实现动态规划求解点数值三角形的最优路径  在一个n行的点数值三角形中,寻找从顶点开始每一步可沿左斜(L)或右斜(R)向下至底的一条路径,使该路径所经过的点的数值和最小。  应用顺推实现动态规划求解从项到底的最小路程。         (1)建立递推关系  设点数值三角形的数值存储在二维数组a(n,n),数组b(i,j)为从顶点(1,1)到点(i,j)的最小数值和。b(i,j)与stm(i,j)(i=2,3,…,n)的值由b数组的第i-1行的第j-1个元素与第j个元素值的大小比较决定,即有递推关系:  b(i,j)=a(i,j)+b(i-1,j);    (b(i-1,j) 其中i=2,3,…,n  比较b(n,1),b(n,2),…,b(n,n)所得最小值min即为所求的最小路径。 边界条件: b(1,1)=a(1,1);  b(i,1)=a(i,1)+b(i-1,1);     b(i,i)=a(i,i)+b(i-1,i-1);  i=2,…,n。 (2)顺推计算最优值 b[1][1]=a[1][1]; for(i=2;i<=n;i++)    { b[i][1]=a[i][1]+b[i-1][1];        b[i][i]=a[i][i]+b[i-1][i-1];      }  for(i=3;i<=n;i++)       // 顺推得b[n][j]        for(j=2;j<=i-1;j++)      if (b[i-1][j]         b[i][j]=a[i][j]+b[i-1][j-1]; min=10000;  for(j=1;j<=n,j++)      // 比较得最短路程   if(b[n][j] { min=b[n][j];m=j;} printf(\  6-3  应用顺推实现动态规划求解n行m列边数值矩阵最大的路程  已知n行m列的边数值矩阵,每一个点可向右或向下两个去向,试求左上角顶点到右下角顶点的所经边数值和最大的路程。  动态规划算法设计:  设矩阵的行数n,列数m,每点为(i, j),i=1,2,…,n;j=1,2,…,m。显然,该边数值矩阵每行有m?1条横向数值边,每列有n?1条纵向数值边。  从点(i,j)水平向右的边长记为r(i,j)(j 设a(i,j)为左上角顶点(1,1)到点(i,j)的最大路程。 a(i,j)的值由a(i-1,j)+d(i,j)与a(i,j-1)+r(i,j)比较,取其较大者得到,即有递推关系:  a(i,j)=max(a(i-1,j)+d(i-1,j),a(i,j-1)+r(i,j-1))  其中i=2,…,n;j=2,…,m。  注意到左边纵列与上边横行只有惟一出口,因而有边界条件:     a(1,1)=0;  a(i,1)=a(i-1,1)+d(i-1,1);   i=2,…,n a(1,j)=a(1,j-1)+r(1,j-1);   j=2,…,m (2)逆推计算最优值 a[1][1]=0;  for(i=2;i<=n;i++)     a[i][1]=a[i-1][1]+d[i-1][1];     // 左边纵列初始化   for(j=2;j<=m;j++)     a[1][j]=a[1][j-1]+r[1][j-1];     // 上边横行初始化   for(i=2;i<=n;i++)                   // 顺推求解a(i,j)   for(j=2;j<=m;j++)     if(a[i-1][j]+d[i-1][j]>a[i][j-1]+r[i][j-1])       a[i][j]=a[i-1][j]+d[i-1][j];    else