算法设计-枚举

枚举思想:

列出所有的可能解,一一判断是否符合要求

枚举变量,枚举空间;判断条件

🌰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;
}

二分枚举

🌰1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值