【CSP考题扩展】暴力枚举(1)

文章讲述了如何通过编程解决三个问题:计算给定棋盘上正方形和长方形的数量差异,以及寻找满足特定比例的三位数组合。涉及组合数学、递归和素数判断算法的应用。
摘要由CSDN通过智能技术生成

【P2241 统计方形(数据加强版)】

题目链接

解题思路

根据题目描述,通过以下步骤来解决这个问题:

  1. 计算所有可能的正方形的数量。
  2. 计算所有可能的长方形的数量,然后从中减去正方形的数量以得到仅包含长方形的数量。

1.计算所有可能的正方形的数量

在一个 n × m n \times m n×m 的棋盘上,可以构建不同大小的正方形。最小的正方形尺寸是 1x1,最大的正方形尺寸取决于 n n n m m m 中的较小者,因为正方形的两边长度相同。

对于每一个可能的正方形尺寸 i × i i \times i i×i(其中 i i i 从 1 到 min ⁡ ( n , m ) \min(n, m) min(n,m)),棋盘中包含的这种尺寸的正方形数量是由可以选择的起始点数量决定的。如果在棋盘上选择一个点作为正方形的左上角,需要确保有足够的空间在行和列的方向上形成一个完整的正方形。

  • 对于每种大小的正方形,可以在横向上选择 n − i + 1 n - i + 1 ni+1 个不同的位置(因为你需要至少 i i i 行空间来构建一个边长为 i i i 的正方形)。
  • 同样,在纵向上,可以选择 m − i + 1 m - i + 1 mi+1 个不同的位置(因为需要至少 i i i 列空间来构建一个边长为 i i i 的正方形)。

2.计算所有可能的长方形的数量

在一个 n × m n \times m n×m 的棋盘上,可以选择两条不同的横线和两条不同的纵线来确定一个矩形。这个棋盘总共有 n + 1 n + 1 n+1 条横线和 m + 1 m + 1 m+1 条纵线,因为考虑的是棋盘的格子边界。

  1. 选择横线:需要从 n + 1 n + 1 n+1 条横线中选择两条不同的横线来形成矩形的上下边界。这可以通过组合数 C ( n + 1 , 2 ) = ( n + 1 ) × n 2 C(n+1, 2) = \frac{(n+1) \times n}{2} C(n+1,2)=2(n+1)×n 来实现。

  2. 选择纵线:同样地,需要从 m + 1 m + 1 m+1 条纵线中选择两条不同的纵线来形成矩形的左右边界。这可以通过组合数 C ( m + 1 , 2 ) = ( m + 1 ) × m 2 C(m+1, 2) = \frac{(m+1) \times m}{2} C(m+1,2)=2(m+1)×m 来实现。

  3. 组合起来:因为横线的选择和纵线的选择是独立的,可以将两个选择组合起来计算总的矩形数目。这就是通过乘法原则 ( n + 1 ) × n 2 × ( m + 1 ) × m 2 \frac{(n+1) \times n}{2} \times \frac{(m+1) \times m}{2} 2(n+1)×n×2(m+1)×m 来实现的,简化后就是 n × ( n + 1 ) × m × ( m + 1 ) 4 \frac{n \times (n + 1) \times m \times (m + 1)}{4} 4n×(n+1)×m×(m+1)

完整代码

#include <iostream>
using namespace std;

int main() {
    long long n, m;
    cin >> n >> m;

    // 计算正方形的数量
    long long squares = 0;
    for (long long i = 1; i <= min(n, m); i++) {
        squares += (n - i + 1) * (m - i + 1);
    }

    // 计算所有可能的长方形的数量 (包括正方形)
    long long rectangles = n * (n + 1) * m * (m + 1) / 4;

    cout << squares << " " << rectangles - squares << endl;
    return 0;
}

【P2089 烤鸡】

题目链接

解题思路

这个题目是关于组合枚举的。我们需要找到所有可能的方式,将10种配料的质量组合起来,使得它们的总和等于给定的美味程度n。每种配料的质量可以是1到3克。

  1. 首先,检查给定的美味程度n是否在可实现的范围内。因为每种配料至少需要1克,所以最小的n是10(每种配料1克)。最大的n是30(每种配料3克)。如果n不在这个范围内,则没有可能的组合,我们只输出0。

  2. 如果n在可行的范围内,我们需要枚举所有可能的配料组合。这意味着我们需要遍历每种配料可能的重量(1到3克),对于每种配料,都进行这样的遍历。

  3. 每次当我们选定了所有10种配料的质量后,我们检查它们的总和是否等于美味程度n。如果是的话,我们就找到了一个有效的组合,并将它加入到方案列表中。

  4. 最后,我们输出找到的方案的数量和所有方案本身。

完整代码

#include <iostream>
#include <vector>
using namespace std;

struct MySolution
{
    int a, b, c, d, e, f, g, h, i, j;
};
int n, solutions;
vector<MySolution>solutionList;

int main() {
	cin >> n;
	if (n < 10 || n > 30)
	{
		cout << 0;
		return 0;
	}

    // 遍历所有可能的配料组合
    for (int a = 1; a <= 3; ++a) {
        for (int b = 1; b <= 3; ++b) {
            for (int c = 1; c <= 3; ++c) {
                for (int d = 1; d <= 3; ++d) {
                    for (int e = 1; e <= 3; ++e) {
                        for (int f = 1; f <= 3; ++f) {
                            for (int g = 1; g <= 3; ++g) {
                                for (int h = 1; h <= 3; ++h) {
                                    for (int i = 1; i <= 3; ++i) {
                                        for (int j = 1; j <= 3; ++j) {
                                            // 如果当前组合的总和等于美味程度
                                            if (a + b + c + d + e + f + g + h + i + j == n) {
                                                // 打印这个组合
                                                solutionList.push_back({ a,b,c,d,e,f,g,h,i,j });
                                                solutions++;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    cout << solutions << endl;
    for (auto& it : solutionList) {
        cout << it.a << " " << it.b << " " << it.c << " " << it.d << " " << it.e << " " << it.f << " " << it.g << " " << it.h << " " << it.i << " " << it.j << endl;
    }
	return 0;
}

P1618 三连击(升级版)

题目链接

解题思路

基于暴力枚举和全排列的方法来找出所有可能的三个三位数组合,使得它们满足给定的比例 A:B:C。

  1. 初始化和输入读取:代码初始化变量 a, b, c 来存储输入的比例。一个标志变量 output 被初始化为 0(假),用于跟踪是否找到至少一组符合条件的三位数。

  2. 数位分配:代码通过九层嵌套的循环分别为三个三位数的每一位分配一个从 1 到 9 的数字。每一层循环都确保当前选择的数字与之前选择的数字不同,确保了每个数字在每个三位数中只使用一次。

  3. 组合验证

    • 对于每一种可能的数字组合(由九个不同的数字组成的序列),代码通过调用 Str2Int 函数将每三个数字组合成一个三位数,得到三个数 t1, t2, t3
    • 然后检查这三个数是否满足比例关系:b * t1 == a * t2a * t3 == c * t1b * t3 == c * t2。如果这些条件都满足,这三个数就符合题目要求。
  4. 输出结果

    • 如果找到符合条件的三个数,将它们输出,并将 output 标志设为真(1)。
    • 如果所有可能的组合都检查完毕,且没有找到符合条件的组合,则输出“No!!!”。
  5. 返回:最后,代码检查 output 标志。如果标志仍为假(即没有输出任何组合),则输出“No!!!”,表示没有找到符合条件的三个数。

完整代码

#include <iostream>
#include <vector>
using namespace std;

int a, b, c;
bool output = 0;

int Str2Int(int a, int b, int c)
{
	return a * 100 + b * 10 + c;	
}

int main() {
	cin >> a >> b >> c;

	for (size_t a1 = 1; a1 < 10; a1++)
	{
		for (size_t a2 = 1; a2 < 10; a2++)
		{
			if (a2 != a1)
			{
				for (size_t a3 = 1; a3 < 10; a3++) 
				{
					if (a3 != a1 && a3 != a2)
					{
						for (size_t a4 = 1; a4 < 10; a4++)
						{
							if (a4 != a1 && a4 != a2 && a4 != a3)
							{
								for (size_t a5 = 1; a5 < 10; a5++)
								{
									if (a5 != a1 && a5 != a2 && a5 != a3 && a5 != a4)
									{
										for (size_t a6 = 1; a6 < 10; a6++) 
										{
											if (a6 != a1 && a6 != a2 && a6 != a3 && a6 != a4 && a6 != a5)
											{
												for (size_t a7 = 1; a7 < 10; a7++)
												{
													if (a7 != a1 && a7 != a2 && a7 != a3 && a7 != a4 && a7 != a5 && a7 != a6)
													{
														for (size_t a8 = 1; a8 < 10; a8++)
														{
															if (a8 != a1 && a8 != a2 && a8 != a3 && a8 != a4 && a8 != a5 && a8 != a6 && a8 != a7)
															{
																for (size_t a9 = 1; a9 < 10; a9++)
																{
																	if (a9 != a1 && a9 != a2 && a9 != a3 && a9 != a4 && a9 != a5 && a9 != a6 && a9 != a7 && a9 != a8)
																	{
																		int t1 = Str2Int(a1, a2, a3), t2 = Str2Int(a4, a5, a6), t3 = Str2Int(a7, a8, a9);
																		if ((b * t1 == a * t2) && (a * t3 == c * t1) && (b * t3 == c * t2))
																		{
																			output = 1;
																			cout << t1 << " " << t2 << " " << t3 << endl;
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	if (!output) {
		cout << "No!!!";
	}
  
	return 0;
}

【P1036 [NOIP2002 普及组] 选数】

题目链接

解题思路

  1. 输入处理:首先,程序从标准输入读取两个整数n和k,分别代表总数和需要选择的数的个数。接着,程序读取n个整数并存储在一个向量(vector)nums中,这些整数就是后续需要进行组合求和的数。

  2. 辅助函数:

    • isPrime: 一个用于判断一个数是否为素数的函数。它通过遍历从2到sqrt(num)的所有整数来检查num是否有除1和它本身以外的因数。如果有,则该数不是素数;否则,是素数。
    • combine: 一个递归函数,用于实现从nums中选取k个数的所有可能组合,并计算它们的和。此函数采用了四个参数:
      • nums:包含所有待组合数的向量。
      • start:当前递归应从哪个索引开始。
      • k:还需要选择的数的数量。
      • sum:当前已选数的总和。
      • count:满足条件的和(即为素数的和)的数量,是一个引用参数,用于在函数中更新计数。
  3. 递归选择与判断(重要):

    • combine函数中,如果k为0,意味着已经选取了k个数,则会检查当前的sum是否为素数。如果是,count加1。
    • 如果k不为0,则函数遍历nums中从start开始的每个数,对于每个数,函数会递归调用自己,其中start参数更新为i + 1(以避免重复选择同一个数),k减1(因为已选择了一个数),并将sum增加当前选中的数nums[i]
  4. 主函数:

    • main函数中,读取输入后,初始化计数器counter为0。
    • 调用combine函数从索引0开始,尝试所有可能的组合。
    • 最后,输出计数器counter,即为满足条件的组合数。

完整代码

#include <iostream>
#include <vector>
using namespace std;

// 检查一个数是否为素数
bool isPrime(int num) {
    if (num < 2) return false;
    for (int i = 2; i * i <= num; i++) {
        if (num % i == 0) return false;
    }
    return true;
}

// 递归函数,用于选取k个数并计算其和
void combine(vector<int>& nums, int start, int k, int sum, int& count) {
    if (k == 0) { // 如果已经选取了k个数,则检查其和是否为素数
        if (isPrime(sum)) count++;
        return;
    }
    for (int i = start; i <= nums.size() - k; i++) { // 递归选择下一个数
        combine(nums, i + 1, k - 1, sum + nums[i], count);
    }
}
int n, k, counter;

int main() {    
    cin >> n >> k;
    vector<int> nums(n);
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
    }

    combine(nums, 0, k, 0, counter);
    cout << counter << endl;

    return 0;
}
  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值