超星学习通 高级语言程序设计 实验8 结构化程序设计 试题及答案

1. 

题目名称:正整数分解

题目描述:正整数n,按第一项递减的顺序依次输出其和等于n的所有不增的正整数和式。


输入:一个正整数n(1<n≤15)。

输出:每行输出如样例所示,和等于n的不增正整数和式,数字和运算符间无符号,最后一行结尾有一个回车换行符。

样例:

输入:
4
输出:
4=3+1
4=2+2
4=2+1+1
4=1+1+1+1

方案一,这里我最开始用穷举法,但最后过程过于复杂,相较下,方案二更简单,大家可以略过方案一。

#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
#include"string.h"
#include"assert.h"
int a[150][16];
int jud(const int a[][16],int m,int i)//(m为n-k,i为行)
{
	//情况1返回0,情况2返回正数该正数为非0列的位置,情况3返回负数,绝对值为非0列的位置
	if (a[i][m] != 0)
		return 0;
	while(a[i][m] == 0)
	{
		m--;
		assert(m);//不许m==0
	}
	if(a[i][m]==1)
	return m;//m为非0列
	if (a[i][m] != 1)
		return -m;
	else return 200;
}
int sum(const int a[][16], int i,int m)//i为行数,m为需求和的列数(即n-k+1)
{
	int n;
	int sum1 = 0;
	for (n = 0; n < m; n++)
	{
		sum1 += a[i][n];
	}
	return sum1;
}
//input处理正整数分解时第一个分解出的数(即等号右边第一个数)相同的情况,分解正数,n一共处理n-1次,将分解出的数据(即n的分解等式右端不包括+的部分)填入数组a【】【】
int input(int n,int k,int i)//(k为分解后的式子的第一个数,i为待填充行,每次填完一行i++,返回待填充行)
{
	int m,z;
	a[i][0] = k; a[i][1] = n-k;
	//穷举法处理
	//(1)判断除等号右边第一个数k外,至多有n-k个数,且不大于n-k,人工填入一行(n=(n-k)+k的数据n-k和k)后,剩余的不大于n-k-1
	//(2)数据准备(这里确保穷举的数据递减)
	// 如果k>=n-k
	// 将a的第i行除第0列外的n-k列填入n-k-1,
	// 如果k<n-k
	// 则第i行除0列外n-k列填k
	//(3)穷举(在保证数据递减的情况下枚举并判断)
	//每次从最后一列(即第n-k列(有0列))减一,每次减一后判断是否该行和为n,若为n就是一种可能,数据填入该行,准备下次填入
	//这有三种情况
	//1、最后一列非0,正常减一
	//2、最后一列为0,依次向前寻找非0列,找到第一个非0列为1,则该列减一,
	//3、最后一列为0,依次向前寻找非0列,找到第一个非0列不为1,则减一后将减一后的该数据往后赋值,直到第n-k列被赋值
	//上述3种情况通过限制递减,可保证每种正确情况均可考虑
	//(4)所有数据存入a[][]数组,即可打印
	if (n - k > 2)
	{
		if (k >= n - k)
		{
			i++;
			for (m = 1; m <= n - k; m++)
			{
				a[i][m] = n - k - 1;
			}
		}
		else {
			for (m = 1; m <= n - k; m++)
				a[i][m] = k;
		}
		while (a[i][1] != 1)
		{
			assert(i!=200);
			a[i][0] = k;//先填充第一个数
			while (sum(a, i, n - k + 1) != n)
			{
				z = jud(a, n - k, i);
				assert(z != 200);
				if (z == 0)
				{
					a[i][n - k ]--;
				}
				if (z < 0)
				{
					z = -z;
					a[i][z]--;
					for (m = z + 1; m <= n - k; m++)
					{
						a[i][m] = a[i][z];
					}
					z = -z;
				}
				if (z > 0)
					a[i][z]--;
			}
			if (a[i][1] == 1)//因为考虑不周导致while(a[i][1]限制不完全
//,使这个if语句后面代码在不和条件时多执行了一次,因此这里用if限制一下
				return i + 1;
			i++;
			memcpy(a[i], a[i - 1], 16 * sizeof(int));
			z = jud(a, n - k, i);
			assert(z != 200);
			if (z == 0)
			{
				a[i][n - k]--;
			}
			if (z < 0)
			{
				z = -z;
				a[i][z]--;
				for (m = z + 1; m <= n - k; m++)
				{
					a[i][m] = a[i][z];
				}
				z = -z;
			}
			if (z > 0)
				a[i][z]--;
		}
		return i;
	}
	if (n - k == 2)
	{
		i++;
		a[i][0] = k; a[i][1] = a[i][2]=1;
		return i+1;
	}
	if (n - k == 1)
	{
		return i + 1;
	}
	else return -1;
}
int main()
{
	bool flag = true;
	int n,c=0,i=0;
	scanf("%d",&n);
	if (n == 1)
	{
		printf("1=1\n");
		return 0;
	}
	for (int l = n-1; l > 0; l--)
	{
		c = input(n,l,c);
		assert(c != -1);
	}
	for (int l = 0; l <=c; l++)
	{
		i = 0;
		while (a[l][i] != 0)
		{
			if (flag)
			{
				printf("%d=%d", n,a[l][i]);
				flag = 0;
			}
			else printf("+%d",a[l][i]);
			i++;
			if (a[l][i] == 0)
			{
				printf("\n");
				flag = 1;
			}
		}
	}
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int a[20] = {0};
void F(int sum, int ub, int p)
{
	if (sum == 0) {
		printf("%d", a[0]);
		printf("%c",a[1]);
		for (int j = 2; j <= p - 2; j++)
		{
			printf("%d+",a[j]);
		}
		printf("%d",a[p-1]);
		printf("\n");
		
	}
	for (int i = sum; i >= 1; i--)
	{
		if (i <= ub)
		{
			a[p] = i;
			F(sum - i, i, p + 1);
		}
	}
}
int main()
{
	int m;
	scanf("%d",&m);
	a[0] = m; a[1] = '=';
	F(m, m - 1, 2);
	printf("\n");
	return 0;
}

  2. 

题目名称:N皇后问题

题目描述:

八皇后问题由高斯(C. F. Gauss)最早在1850年提出并研究,但并未完全解决。N皇后问题指在一个N×N的棋盘上放置N个皇后,使任意两个皇后都不能互相攻击。按国际象棋规则,两个皇后,若在同一行上,或在同一列上, 或在同一条斜线上, 则她们可以互相攻击。下图即满足八皇后条件的一种棋局。

编写程序给出满足条件的棋局数目。

Exp08-Basic02.jpg

输入:一个正整数N(0<N≤13)输出:棋局数目
样例1:

输入:
2
输出:
0

样例2:

输入:
8
输出:
92
#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
int z = 0;
int arr[14], m,t=0;
int check(int r)//判断数组数据是否合适,合适返回1,否者返回0
{
	int i;
	for (i = 1; i < r; i++)
	{
		if (arr[r] == arr[i])return 0;
		if (arr[r] - r == arr[i] - i)return 0;
		if (arr[r] + r == arr[i] + i)return 0;
	}
	return 1;
}
void extend(void)
{
	m++;
	arr[m] = 1;
}
void  out(void)
{
	t++;
}
void change( int N)
{
	while (arr[m] == N&&m>0)
	{
		m--;
	}
	if (arr[m] < N&&m>0)
		arr[m]++;
}
int main()
{
	arr[0] = 0; arr[1] = 1; m = 1;
	int N;
	scanf("%d", &N);
	if (N == 1)
	{
		printf("1"); return 0;
	}
	while (m > 0)
	{
		if (check(m))
		{
			if (m == N)
			{
				out();
				change(N);
			}
			else extend();
		}
		else change(N);
	}
	printf("%d",t);
	return 0;
}

这道题用递归可能由于运算时间过长导致不通过,所以可以不用递归还是少用为妙

3. 

题目名称:八皇后本质不同的解

题目描述:

如上题所述,当N=8时,一共有92种可能。如果去除其中上下对称、左右对称棋局、主副对角线对称棋局和旋转后重复棋局,则有12种完全不同的棋局。编写程序,输出这12种棋局。

输入:

输出:

共12行,每行输出1种棋局,

例如,第一行输出 No1:1 5 8 6 3 7 2 4(冒号为西文冒号且前后无多余字符,冒号后的每个数字后均有一个西文空格),

其中No1 表示这是第1种棋局;后续数字序列表示八皇后所在位置,数值本身表示某个皇后在棋盘上的行坐标,该数值所在位置表示该皇后的列坐标(>0),例如,数字5位于序列的第2位,表示棋盘上第5行第2列有一个皇后;数字4位于序列的第8位,表示棋盘上第4行第8列有一个皇后,由此,这8个数字描述了一种棋局。12种棋局的输出顺序:字典序(参考样例)。

样例:

输入:(无)
输出:
No1:1 5 8 6 3 7 2 4
No2:1 6 8 3 7 4 2 5
……(此处省略10行,分别表示No3至No12棋局)

#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
#include"string.h"
int z = 0;
int arr[9], k[100][9], j[100][9] = {0}, m, t = 0;
int check(int r)//判断数组数据是否合适,合适返回1,否者返回0
{
	int i;
	for (i = 1; i < r; i++)
	{
		if (arr[r] == arr[i])return 0;
		if (arr[r] - r == arr[i] - i)return 0;
		if (arr[r] + r == arr[i] + i)return 0;
	}
	return 1;
}
void extend(void)
{
	m++;
	arr[m] = 1;
}
void  out(void)
{
	t++;
}
void change(int N)
{
	while (arr[m] == N && m > 0)
	{
		m--;
	}
	if (arr[m] < N && m>0)
		arr[m]++;
}
void copy(void)
{
	while (m > 0)
	{
		if (check(m))
		{
			if (m == 8)
			{
				out();
				memcpy(k[t], arr, 9 * sizeof(int));
				change(8);
			}
			else extend();
		}
		else change(8);
	}
}
void _memmove(void)//用于将最终结果移到j数组中
{
	int q = 0;
	for (int n = 1; n <= 92; n++)
	{
		if (k[n][0] == 0)
		{
			memmove(j[q], k[n], 9 * sizeof(int));
			q++;
		}
	}
}
int check(int c,int i, int n,int m)
{
	switch (c)
	{
	case 1: return ((k[i][m] + k[n][m]) == 9);//检查左右对称
	case 2:return (k[i][m] == k[n][9 - m]);//检查上下对称
	case 3:return (k[n][k[i][m]] == m);//检查主对角线对称
	case 4:return (k[n][9 - k[i][m]] == 9 - m);//检查副对角线对称
	case 5:return (k[n][9 - k[i][m]] == m);//检查顺时针转90°
	case 6:return (k[n][9 - m] == 9 - k[i][m]);//检查顺时针转180°
	case 7:return (k[n][k[i][m]] == 9 - m);//检查逆时针转90°

	}
}
void jud(int(*p)(int ,int ,int,int))//用于循环并判断对称情况
{
	int i = 1, m = 1, n = 1,c=1;
	for (c = 1; c <= 7; c++)
	{
		for (i = 1; i < 92; i++)
		{
			for (n = i + 1; n <= 92; n++)
				for (m = 1; m <= 8; m++)
				{
					if (p(c, i, n, m))
					{
						if (m == 8)
							k[n][0] = 1;//这里判断出为重复情况,k[n][0]置1表示重复
					}
					else break;
				}
		}
	}
}
int main()
{
	arr[0] = 0; arr[1] = 1; m = 1;
	copy();//用于生成数据并copy到k数组中
	int q = 1;
	jud(check);
	_memmove();
	for (q = 0; q < 12; q++)
	{
		printf("No%d:", q+1);
		for (int b = 1; b < 9; b++)
		{
			printf("%d ", j[q][b]);
			if (b == 8)
				printf("\n");
		}
	}
	return 0;
}

这里我使用教材上的试探法得出初始数据,但听说用DFS算法生成初始的未排序数据更快更简单,有兴趣可以了解一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值