枚举思想:
列出所有的可能解,一一判断是否符合要求
枚举变量,枚举空间;判断条件
🌰1.特殊的素数肋骨
基础枚举思想
#include<iostream>
#include<math.h>
using namespace std;
//main idea:列出所有解 + 判断素数->判断特殊素数
bool IsPrime(int x)//module:判断素数
{
if(x <= 1)
return false;
for(int i = 2; i*i <= x; i++)
if(x % i == 0)
return false;
return true;
}
bool IsSpePrim(int x, int n)//idea:从后往前拆分
{
while(n--)//tip:循环n次
{
if(IsPrime(x))
x /= 10;
else return false;
}
return true;
}
int main()
{
int N; int x;
cin >> N;
for(x = pow(10.0, N-1); x < pow(10.0, N); x++)
//idea:枚举所有的N位数
{
if(IsSpePrim(x, N))
cout << x << endl;
}
}
但是,只有60分,会超时。
在 列出所有可能解的步骤 用递归改进
主思想: 从前往后,边构造可能解边判断。
数学分析:
第一位只能是素数:2,3,5,7
其余位:不能是偶数,不能是5,因此只有1,3,7,9
代码实现:利用DFS递归构造可能解
module: 判断素数:
bool IsPrime(int x)//module:判断素数
{
if(x <= 1) return false;
for(int i = 2; i*i <= x; i++)
if(x%i == 0)
return false;
return true;
}
无数学分析版:
#include <iostream>
using namespace std;
int N;
void DFS(int num, int n)
{
if(N == n)
{
cout << num << endl;
return ;
}
for(int i = 1; i <=9; i++)
{
if(Isprime(num*10+i))
DFS(num*10+i, n+1);
}
}
int main() {
cin >> N;
DFS(0,0);
return 0;
}
数学分析版:
#include <iostream>
using namespace std;
//main idea:利用递归,从前往后,边构造边判断
int N;
void DFS(int num, int n)
{
if(N == n)
//递归出口:找到了解--N位数的特殊素数
{
cout << num << endl;
return ;
}
for(int i = 1; i <=9; i+=2)
//除了第一位,其余位只能是 1,3,7,9
{
if(i == 5) continue;//tip!:用continue剔除5
if(IsPrime(num*10 +i))
//如果前几位符合,才继续往下构造可能解
DFS(num*10 +i, n+1);
}
}
int main() {
cin >> N;
DFS(2, 1); DFS(3,1);DFS(5,1);DFS(7,1);
//第一位只能是素数,都从第一位开始构造
return 0;
}
🌰2.最小差距
分析:首先将所有数字从小到大排序,接着这道题可以分三种情况考虑:
(1)最简单情况: 只有两个数,直接两数相减取绝对值即可。
(2)奇数个数的情况:首先保证第一个数字非0,如果为0,就将第一个数与第二个数交换位置,然后从左往右连续取n/2+1个数组成第一个整数,最后从右往左连续取n/2个数,即剩下的所有数组成另一个整数。这样所求得的一对整数的差的绝对值最小。此时可以直接构造出差的绝对值最小的两个数
(3)偶数个数的情况:此时两个数位数相同,所以得枚举构造后找到min。从左往右依次枚举,首先保证第一个数非0,如果为0,就从下一个数开始枚举;取第k个数作为第一个整数的第一个数字,取第k+1个数作为第二个整数的第一个数字;然后,对于剩下的数字,从右往左连续取n/2-1个数加入第一个整数中,从左往右连续取n/2-1个数加入第二个整数中;最后,取差的绝对值最小的一对整数。
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int main()
{
int T,N,a[20];//数组大小大一些
cin >> T;
while(T --)
{
int x= 0,y= 0;
cin >> N;
for(int i = 1; i <= N; i++)
cin >> a[i];
sort(a+1, a+N+1);//从小到大排序
//只有两个数字的特殊情况先考虑:
if(N == 2)
cout << abs(a[2]-a[1]) << endl;
else if(N % 2 == 1)
{
//第一位数不能是0,又给定的是不同的数字
if(a[1] == 0) swap(a[1], a[2]);
//从左往右的前N/2+1个数组成第一个整数(尽量小)
for(int i = 1; i <= N/2 +1; i++)//idea!
x = x*10 + a[i];
//从右往左的后N/2个数组成第二个整数(尽量大)
for(int j = N; j >= N/2 +2 ; j--)
y = y*10 +a[j];
cout << abs(x-y) << endl;
}
else
{
int res = 0x7fffffff;
//idea!:x,y位数相同->需要枚举再找最小
//枚举变量1与其枚举空间:取第i个数作为x的最高位,取第i+1个数作为y的最高位
for(int i = 1; i < N; i++)//i+1<+N
{
if(a[i] == 0)
continue;
x = a[i];
y = a[i+1];
//枚举变量2与其枚举空间:还需要构造N/2-1位数:
int k = N/2-1;
int r = N, l = 1;
//从右往左的数字给x,左往右的数字给y
while(k--)
{
//tip:不能用重复的数字a[i]和a[i+1]
if(r == i+1 ) r --;
if(r == i) r--;//r--后,依然可能==i;
if(l == i) l++;
if(l == i+1) l++;//l++后,依然可能==i+1
x = x*10 + a[r];
y = y*10 + a[l];
r--;
l++;
}
res = min(res, abs(x-y));
}
cout << res << endl;
}
}
return 0;
}
🌰3.最大公约数和最小公倍数
例:3 60 有:3 60,12 15, 15,12,60 3共4组
记忆:两个数x,y;设他们的最大公约数,最小公倍数分别为a,b -> x*y = a*b
枚举变量与枚举空间:a<= x <=b
判断条件:由公式得y = a*b/x
判断1.但是,由于int除法的特点,比如60/7 == 8,所以此时的y必须还得判断
x*y == a*b?
判断2.比如60/4 == 15,满足判断1,但是,4和15的最小公约数不是3,因此还需判断gcd(x,y)==a?
module :求最大公约数,最小公倍数
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int gcd(int x, int y)//module:辗转相除求最大公约数;最小公倍数=xy/gcd
{
if(y == 0)
return x;
else return gcd(y, x%y);
}
int main()
{
int a,b,x,y;
cin >> a >> b;
int count = 0;
for( x = a; x <= b; x++)
{
y = a*b/x;//由最大公约数*最小公倍数==xy求出另一个整数
if(y*x == a*b && gcd(x,y) == a)
//tip!:int除法的特点,必须得再判断x*y是否==a*b
//还需判断最小公约数
count ++;
}
cout << count << endl;
return 0;
}
🌰4.盒子
审题:一个盒子可以同时放进 两种球(不是两个球!)
分析: 球除了颜色没有任何区别->分别算红/蓝球有几种放法,最后相乘即得答案。分别记为sum1,sum2 -> sum = sum1*sum2.
以红球为例, 题目中球不必全部放入盒子中-> 枚举变量设为取i个红球去放,设i个红球有ti种放法 ,再把 i== 0单独算,t0 = 1,,sum1初始值 = t0,则枚举空间为[1,A]。
插板法 : 现在分析i个红球有几种放法->排列组合之插板法:
https://blog.csdn.net/hnjzsyjyj/article/details/119618836
所以,有i+n-1个间隔,放n-1个板,
数学递推: 又由数学公式递推推导得 = (i+n)/(i+1) *
代码实现可为:t1 = (i-1+n)/i * t1
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef unsigned long long ULL;
int main()
{
int n, a, b;
cin >> n >> a >> b;
ULL t1=1,t2=1,sum1 = t1, sum2 =t2;
for(int i= 1; i <= a; i++)
{
t1 = t1 * (i-1+n)/i;//tip!
sum1 += t1;
}
for(int j = 1; j <= b; j++)
{
t2 = t2 * (j-1+n)/j;
sum2 += t2;
}
ULL sum = sum1 * sum2;
cout << sum << endl;
return 0;
}