面向问题求解的高级程序设计:(一)枚举

13 篇文章 0 订阅

系列文章目录

面向问题求解的高级程序设计:
(一)枚举
(二)查找
(三)贪心算法
(四)搜索
(五)分治与递归
(六)动态规划
(七)最短路
(八)最小生成树
(九)数论
(十)期中回顾



前言

      《面向问题求解的高级程序设计》——这是我们学校开设的一门创新实验课,我也是第一次学习算法的有关内容,所以这次的系列文章我将会把在这期间做的例题整理下来,作为学习笔记。同时非原创的答案我都会在相应的位置标明出处,本人实力有限,还请多多包涵,文章若有不对的地方也欢迎各位批评指正。
       2021.03.20开始把想法付诸实践,打算把这门课学完以后再把系列文章集中发表。
       2021.04.18基本上结课了,还是感觉有些难不大会。
       2021.07.07考完期末试了,打算再简单修改一下,然后再小学期的时候发表,具体理解的内容以后有时间再充实吧。(变相弃坑了qwq)
       2021.07.09如果有空的话就把这些东西再学一遍,这次系列文章按照目前的完成度只能算作是一个提纲,有具体的方向但是没有具体的知识点(还要自己学习),主要还是在理解了知识点以后通过做题来掌握知识点,提高使用的熟练度。
2021.12.04已经开始走这条路了,虽然现阶段的实力有待提升,但寒假刷题的时候会回来看看的,会在目前能力的基础上不断完善,会针对性的写一些题解,记录一下自己的心得

这些算法都是ACM比赛做题里会用到的,所以简单的写一些相关的内容(当然,大部分的内容都来自讲课的PPT)

●ACM规则

1.只能携带纸质资料

2.排名优先题数,相同题数的罚时越少排名越高。

3.每一题的罚时是从比赛开始时到第一次正确提交时所用的时间。在第一次正确提交之前,每次错误提交会额外罚时20分钟。

●常见错误原因

WAWrong Answer

最常见的错误。

CECompile Error

编译错误,一般是你语法问题或是语言选错了。一般来讲ce不会计算在罚时之内。

TLETime Limit Exceeded

时间超限。算法需要更换或者优化。

MLEMemory Limit Exceeded

内存超限。同上。

RERuntime Error

运行错误。数组越界、除0或者栈溢出等。

●时间复杂度

算法时间复杂度:针对指定基本运算,算法所做的运算次数

基本运算:比较,四则运算,交换……

输入规模:题目中所给出的数据规模

O(n)

for i = 1 to n do
	print(i)

O(n^2)

for i = 1 to n do
	for j = 1 to n do
		print(a[i,j])

O(n^2)

for i = 1 to n do
	a = a + 1
	for j = 1 to n do
		b = b + j
		c = c + b
		d = b * 2

●最坏时间复杂度

由于所给数据不确定,算法的实际基本运算次数是会随着数据变化而变化的。因此在所给数据非常极端的情况下,算法的时间复杂度会退化。

●注意事项

枚举时注意选好枚举空间,一个合理的枚举空间能让你的枚举范围与次数都大大减小。

注意判断条件。

当数据量过大时慎用枚举。

一、枚举法

基本思想:

枚举也称作穷举,指的是从问题所有可能的解的集合中一一枚举各元素。

用题目中给定的检验条件判定哪些是无用的,哪些是有用的。能使命题成立。即为其解。

枚举法优缺点:

优点: 算法简单,在局部地方使用枚举法,效果会十分的好

缺点: 运算量过大,当问题的规模变大的时候,循环的阶数越大,执行速度越慢。计算量容易过大

二、例题部分

A.水仙花数

 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的:
“水仙花数”是指一个三位数,它的各位数字的立方和等于其本身,比如:153=1^3+5^3+3^3。
现在要求输出所有在m和n范围内的水仙花数。

Input
输入数据有多组,每组占一行,包括两个整数m和n(100<=m<=n<=999)。

Output
对于每个测试实例,要求输出所有在给定范围内的水仙花数,就是说,输出的水仙花数必须大于等于m,并且小于等于n,如果有多个,则要求从小到大排列在一行内输出,之间用一个空格隔开;
如果给定的范围内不存在水仙花数,则输出no;
每个测试实例的输出占一行。

Sample Input

100 120
300 380

Sample Output

no
370 371

代码如下:

#include <stdio.h>
#include <stdlib.h>
int judge(int i)
{
    int a = i%10;
    int b = i/100;
    int c = i/10%10;
    if(i==a*a*a+b*b*b+c*c*c)
        return 1;
    else
        return 0;
}
int main()
{
    int m,n,t,i,flag=0,flag1=0;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        flag1=0;
        flag=0;
        for(i=m; i<=n; i++)
        {
            t = judge(i);
            if(t)
            {
                if(flag1)
                    printf(" ");
                printf("%d",i);
                flag = 1;
                flag1 = 1;
            }
        }
        if(!flag)
        {
            printf("no");
        }
        printf("\n");
    }

    return 0;
}

B.亲和数

古希腊数学家毕达哥拉斯在自然数研究中发现,220的所有真约数(即不是自身的约数)之和为:

1+2+4+5+10+11+20+22+44+55+110=284。

而284的所有真约数为1、2、4、71、 142,加起来恰好为220。人们对这样的数感到很惊奇,并称之为亲和数。一般地讲,如果两个数中任何一个数都是另一个数的真约数之和,则这两个数就是亲和数。

你的任务就编写一个程序,判断给定的两个数是否是亲和数 

Input
输入数据第一行包含一个数M,接下有M行,每行一个实例,包含两个整数A,B; 其中 0 <= A,B <= 600000 ;

Output
对于每个测试实例,如果A和B是亲和数的话输出YES,否则输出NO。

Sample Input

2
220 284
100 200

Sample Output

YES
NO

代码如下:

#include <stdio.h>
#include <stdlib.h>
#define N 40

int main()
{
    int m;
    int num[N];
    int a,b,j,i,i0;
    int sum,sum1;
    scanf("%d",&m);
    for(j=0;j<m;j++)
    {
        scanf("%d%d",&a,&b);
        sum = 0;
        i0=0;
        for(i=1; i<a; i++)
        {
            if(a%i==0)
            {
                num[i0]=i;
                sum+=num[i0];
                i0++;
            }
        }
        i0=0;
        sum1=0;
        for(i=1; i<sum; i++)
        {
            if(sum%i==0)
            {
                num[i0]=i;
                sum1+=num[i0];
                i0++;
            }
        }
        if (a==sum1&&b==sum)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

C.鸽子数

 通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
鸽子数字由以下过程定义:从任何正整数开始,将数字替换为其各个数位的平方和,并重复该过程,直到该数字等于1。如果不能,则这个数字不是鸽子数。
例如7是鸽子数,因为7->49->97->130->10->1。(7*7=49,4*4+9*9=97,9*9+7*7=130....如此类推)
显然1是第一个鸽子数。
有Q个询问,每个询问给出一个数k,你需要输出第k个鸽子数。

Input
第一行一个Q,代表询问的个数(Q<=100000)
接下来Q行,每行一个数字k(k<150000)

Output
每行输出一个数,代表第k个鸽子数

Sample Input

2
1
2

Sample Output

1
7

代码如下:

#include <stdio.h>
#include <stdlib.h>
#define N 100000
#define NN 150000

int judge(long long a)//计算鸽子数
{
    long long b,k=0;
    int count=0;
    while(1)
    {
        while(a)
        {
            b=a%10;
            k=k+b*b;
            a/=10;
        }
        if(k==1)//是鸽子数
        {
            return 1;
        }
        a = k;
        k = 0;
        count++;
        if(count==19)
            break;
    }
    return 0;
}
int main()
{
    int q;
    int num[N]={0};
    int gege[NN]={0};
    scanf("%d",&q);
    int i,j;
    int end = 0;
    for(i=1; i<=q; i++)
    {
        scanf("%d",&num[i]);
        if(num[i]>end)
        end = num[i];
    }
    for(i=1,j=1; i<=end; j++)
    {
        if(judge(j))
        {
            gege[i]=j;
            i++;
        }
    }
    for(i=1; i<=q; i++)
    {
        printf("%d\n",gege[num[i]]);
    }
    return 0;
}

D.Switches and Lamps

You are given n switches and m lamps. The i-th switch turns on some subset of the lamps. This information is given as the matrix a consisting of n rows and m columns where ai, j = 1 if the i-th switch turns on the j-th lamp and ai, j = 0 if the i-th switch is not connected to the j-th lamp.

Initially all m lamps are turned off.

Switches change state only from "off" to "on". It means that if you press two or more switches connected to the same lamp then the lamp will be turned on after any of this switches is pressed and will remain its state even if any switch connected to this lamp is pressed afterwards.

It is guaranteed that if you push all n switches then all m lamps will be turned on.

Your think that you have too many switches and you would like to ignore one of them.

Your task is to say if there exists such a switch that if you will ignore (not use) it but press all the other n - 1 switches then all the m lamps will be turned on.

Input

The first line of the input contains two integers n and m (1 ≤ n, m ≤ 2000) — the number of the switches and the number of the lamps.

The following n lines contain m characters each. The character ai, j is equal to '1' if the i-th switch turns on the j-th lamp and '0' otherwise.

It is guaranteed that if you press all n switches all m lamps will be turned on.

Output

Print "YES" if there is a switch that if you will ignore it and press all the other n - 1 switches then all m lamps will be turned on. Print "NO" if there is no such switch.

Examples
Input

4 5
10101
01000
00111
10000

Output

YES

Input

4 5
10100
01000
00110
00101

Output

NO

代码如下:

#include <iostream>

using namespace std;


long long matrix[2007][2007];//放在外面就好了
long long sum[2007];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int flag = 0;
    int i,j,a,b;
    int k=1;
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=m; j++)
        {
            scanf("%1d",&matrix[i][j]);
            sum[j] += matrix[i][j];
        }
    }
    for (i=1; i <= n; i++)
    {
        for (j = 1; j <= m; j++)
            if (sum[j] - matrix[i][j] == 0) break;
        if (j > m)
        {
            printf("YES");
            return 0;
        }
    }
    printf("NO");
    return 0;
}


参考文章:Codeforces-985B - Switches and Lamps - 思维

E.权利指数

参考文章:HDU - 1557 权利指数

F.接收外星来信

参考文章:08:Calling Extraterrestrial Intelligence AgainMOOC程序设计算法基础期末第八题

G.魔表

参考文章:POJ1166 The Clocks (爆搜 || 高斯消元)

H.三国杀

参考文章:SanguoSHA - HDU 4068 暴力枚举


三、附加

寻找素数:

for i = 2 to n-1 do
	if n % i == 0
		return false
return true
for i = 2 to sqrt(n) do
	if n % i == 0
		return false
return true

选讲例题:

2019CCPC秦皇岛站热身赛A题:

定义sum(x)为x的各位数字之和。现给任意正整数k,使得对于每个正整数k找到数x使得k*sum(x)=x。
k<1e9

思路

假设x的位数为n,那么sum(x)最大为9*n,由于k<1e9,所以sum(x)不会大于100。因	此只要从k枚举到100k,看是否满足条件即可。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

竹清兰香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值