计算机考研机试指南第二版(王道)——第二章 暴力求解

暴力求解:

  • 2.1 枚举
      • 例题2.1 abc
      • 例题2.2 反序数
      • 例题2.3 对称平方数
      • 习题2.1 与7无关的数 (!!!)
      • 习题 2.2 百鸡问题(!!!)
      • 习题2.3 Old Bill
  • 2.2 模拟
    • 1. 图形排版
      • 例题2.4 输出梯形
      • 例题2.5 叠筐
    • 2. 日期问题
      • 例题2.6 输入年月日,计算该天是今年的第几天
      • 例题 2.7 打印日期
      • 例题2.8 日期累加
      • 例题2.9 剩下的树
      • 例题2.10 手机键盘
      • 习题 2.7

2.1 枚举

例题2.1 abc

**题目描述:**a、b、c均为0~9的数字,求满足abc + bcc = 532的所有值。

#include 
#include 
using namespace std;


int main()
{
   int a, b, c;
   for (a = 0; a < 10; a ++) {
    for (b = 0; b < 10; b ++) {
        for (c = 0; c < 10; c ++) {
            if (a * 100 + b * 110 + c * 12 == 532)  {
                printf("%d %d %d",a, b, c);
                //<
            }
        }
    }
   }
    return 0;
}

例题2.2 反序数

题目描述:N是一个4位数,他的9倍恰好是其反序数,求N的值。
思路:首先考虑反序数的求法,四位数对10进行取整,取到个位数值,然后再/10,依次取到十百千位上的数值。每一次都乘以10+上一位取出来的数值。
计算机考研机试指南第二版(王道)——第二章 暴力求解_第1张图片

#include 
#include 
using namespace std;

//首先定义一个求逆序数的函数
int number_reverse(int n) {
    int remain;//存放整除后的值
    int _reverse = 0;//存放逆序数
    while (n != 0) {//4321->432->43->4
        remain = n % 10; //1->2->3->4
        _reverse = _reverse * 10 + remain;//1->12->123->1234
        n = n / 10;
    }
    return _reverse;
}
int main() {
    int n;
    for (n = 1000; n <= 9999; n ++) {
        if (n == 9 * number_reverse(n) ) {
            printf("%d\n", n);
            //cout<
        }
    }


    return 0;
}

例题2.3 对称平方数

题目描述:打印所有不超过256,平方具有对称性质的数。如2和11就是这样的数,因为22=4,1111=121
思路:所谓的对称数就是逆序数等于自身的数,这道题的意思就是求一个数
例如数为A,满足A^2 == τ(A^2)。

#include 
#include 
using namespace std;
//例题2.3,对称平方数1
//打印所有不超过256,其平方具有对称性质的数。即一个数的平方等于平方的逆序数。

//用于求所有不超过256的数的逆序数
求n的逆序数
//int count_reverse(int n) {
//    int _reverse = 0; //用于存放逆序数
//    int remain; //存放余数
//    while(n > 0) {
//        remain = n % 10;
//        n = n / 10;
//        _reverse = _reverse*10 + remain;
//    }
//    return _reverse;
//}
//int main()
//{
//    int n;
//    for(n = 0; n <= 256; n++) {
//        int res = count_reverse(n*n);
//        if(res == n*n){
//            cout<
//        }else continue;
//    }
//    return 0;
//}


//求逆序数
int number_reverse(int n) {
    int remain;
    int _reverse = 0;
    while (n > 0) {
        remain = n % 10;
        _reverse = _reverse * 10 + remain;
        n /= 10;
    }
    return _reverse;
}


int main() {
    for (int i = 0; i < 265; i ++) {
        if (i * i == number_reverse(i*i)) {
            printf("%d\n", i);
        }
    }
}

习题2.1 与7无关的数 (!!!)

题目描述: 一个正整数,如果能被7整除或者其十进制表示法中某个位数上为7,则称其为与7有关的数。求所有小于等于n(n < 100)的与7无关的正整数的平方和。
思路:重点在于如何求与7无关的数。

#include 
#include 
using namespace std;

//习题2.1和7无关的数

//构造一个函数用于判断输入的整数是否和7有关
int Judge(int n) {
    int flag = 0;
    if (n % 7 == 0) {
        flag = 1;
    }
    while (n > 0) {
        if (n % 10 == 7) {
            flag = 1;
        }
        n /= 10;
    }
    return flag;
}

int main() {
    int n;
    int sum = 0;//用于存放与7无关的正整数的平方和
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i <= n; i ++ ) { //从0开始到n取数
            if (0 == Judge(i)) {//若与7无关
                    sum = sum + i*i;
            }
        }
        printf("%d\n",sum);
    }

    return 0;
}

习题 2.2 百鸡问题(!!!)

题目描述:小于等于n元买100只鸡,大鸡5元,小鸡2元,1/3元一只的小小鸡,分别记为x、y、z只,求所有可能的解x,y,z。
思路:其实就是求三元一次方程的解。

#include 
#include 
using namespace std;

//百鸡问题
//小于等于n元买100只鸡,大鸡5元,小鸡2元,1/3元一只的小小鸡,分别记为x、y、z只,求所有可能的解

//三元一次方程的解
int main() {
    int x, y, z;//整数存放鸡的只数
    float n;//费用
    while (scanf("%f", &n)) {
        for (x = 0; x < 100; x ++) {
            for (y = 0;y < 100-x; y ++) {
                z = 100 - x - y;
                if (15*x+6*y+z <= n) {
                    printf("%d %d %d\n",x, y, z);
                }
            }
        }
    }
    return 0;
}

习题2.3 Old Bill

题目描述:N只火鸡的价格为$ XYZ,火鸡的总数N在1到99之间。价格由五位数组成,两边的数字由于褪色而看不清,所以只能看到中间的三位数。假设第一个数字非零,每只火鸡的价格是整数,并且所有火鸡的价格相同。给定N,X,Y和z,编写一个程序来猜测两边褪色的数字和火鸡的原始价格。如果有多个价格符合题意,那么输出最昂贵的那个。

思路:给定火鸡数量N以及总价中的十百千位数X、Y、Z,然后求万位数i和个位数j。注意总价和单价之间的关系,并且题目要求输出满足条件的火鸡的最大单价。

#include 
#include 

using namespace std;

int main() {
    int n,x,y,z;
    while (scanf("%d \n %d %d %d", &n, &x, &y, &z) != EOF) {
        int price;
        int max = 0,maxi,maxj;
        for(int i = 1; i < 10; i++) {
            for (int j = 0;j < 10; j++) {
                price = i*1e4 + x*1e3 + y*1e2 + z*1e1 + j;
                if (price%n == 0 && price > max*n) {//price满足被n整除, price>
                    max = price / n;//最大单价
                    maxi = i; //最大单价所对应的万位数
                    maxj = j; //最大单价所对应的个位数
                }
            }
        }
        if (max == 0)
        printf("0 \n");
        else
        printf("%d %d %d\n", maxi, maxj, max);
    }
    return 0;
}

不知道为啥在本地的codeblock中无法进行用例输入,在oj中反而通过了。有点摸不着头脑,以后有空再琢磨琢磨,代码一个字没改突然又可以运行了。。。。。。

2.2 模拟

1. 图形排版

例题2.4 输出梯形

题目描述:输入h,输出一个高度为h,上底边长为h的梯形
思路:两种方法,一种是一行一行的分析,并且按规律输出。另外一种则是直接声明一个足够大得二维数组然后再进行填充。计算机考研机试指南第二版(王道)——第二章 暴力求解_第2张图片
方法一:h行,分析每一行的空格数和’ * '的数量,并且用i和h表示。
计算机考研机试指南第二版(王道)——第二章 暴力求解_第3张图片

#include 
#include 
#include 
using namespace std;
int main() {
    int h;
    while (scanf("%d", &h) != EOF) {
        int i;
        for (i = 0; i < h; i ++) {//一行一行输出
            for (int j = 2*h-2*(i-1); j > 0; -- j) {
                //先打印该行的空格
                    printf (" ");
            }
            for (int j = h+2*i; j > 0; -- j) {
                //打印该行的*符号
                printf("*");
            }
            printf("\n");
        }
    }
    return 0;
}

方法二:声明一个足够大的二维数组来做。(!!!)
需要重点掌握,图形类的机试题归根结底就是打印一个二维数组,因此此方法是解决此类问题的通用解法。

#include 
#include 
#include 
using namespace std;

//先声明一个足够大的字符数组
char arr[1000][3000];
int main()
{
    int h;

    while (scanf("%d", &h))
    {
        //先将整个数组赋值为空
        for (int i = 0; i < h; i++)
        {
            for (int j = 0; j < 3*h-2; j++)
            {
                arr[i][j] = ' ';
            }
            arr[i][3*h-2] = '\0';//在每一行的行末填入一个终止符号'\0'
        }

        //在空数组中从第h-1行开始往上填入*号
        int beg = 0;//用于指示每一行从哪里开始填充
        for (int i = h-1; i >= 0; i--)
        {
            int j;
            for (j = beg; j < 3*h-2; j ++)
            {
                arr[i][j] = '*';
            }
            beg = beg + 2;
        }
        //由于每一行的行末有终止符,故可以直接按行打印
        for (int i = 0; i <= h; i ++)
        {
            printf("%s", arr[i]);
            printf("\n");//换行符
        }

    }


    return 0;
}

例题2.5 叠筐

2. 日期问题

例题2.6 输入年月日,计算该天是今年的第几天

方法一:1990.9.20为例,先算1990.1.1 -> 1990.9.1 ->1990.9.20
从1.1到2.1中间的天数恰好是1月的天数。以此类推,1.1->9.1就是1+2+…+8月的天数totalday,
9.20就是今年的第totalday+20天。

总的来说这个方法局限比较大。

#include 
#include 
using namespace std;
//例题2.6 今年的第几天
//输入年月日,计算该天是今年的第几天
//1990.9.20为例
int main()
{
    int year, mon, day;
    int mday[13] = {0,31,28,31,30,31,30,31,30,31,30,31};
    int totalDay[13] = {0}; //存放了从mon月1号到1~12月1号的天数
    for (int mon = 2; mon <= 12; ++mon) {
        //到mon月1日的天数 = 到mon-1月1日的天数+第mon-1月的天数
        totalDay[mon] = totalDay[mon-1] + mday[mon-1];
    }
    while (scanf("%d %d %d",&year, &mon, &day) != EOF) {
        bool isLeap = year%400 == 0 || year%100 != 0 && year%4 == 0;
        if (isLeap == true && mon >= 3) {
            printf("%d\n", totalDay[mon] + day + 1);
        }else {
            printf("%d\n",totalDay[mon] + day);
        }
    }
    return 0;
}

方法二:nextday方法,解决日期问题的万能解法

int main() {
    //输入年月日计算该天是今年的第几天
    int year, mon, day;
    int mday[2][13] ={
                            {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                            {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
                            };//每个月份的天数
    while (scanf("%d%d%d",&year, &mon, &day)) {
            int totalDay = 0; //用于计算累加天数
            int row = (year%400 == 0 || year%100 != 0 && year%4 == 0);//判断是否为闰年
            for (int i = 0; i < mon; i ++) {//加上该月份之前的所有月份天数
                totalDay += mday[row][i];
            }
            totalDay += day;//再加上日
            printf("%d", totalDay);
    }
    return 0;
}

例题 2.7 打印日期

题目描述:给出年份m和一年中的第n天,算出第n天是几月几号。按照yyyy-mm-dd的将对应的日期打印出来。

//方案二(日期的万能方案)
//最重要在于nextday的求法
//例题2.7打印日期
//输入年份和天数n并输出该年第n天的日期  yyyy-mm-dd
int main () {
    int year, n;
    int mday[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};

    //{-1,31,29,31,30,31,30,31,31,30,31,30,31}
    //二维数组分别表示平年闰年的月份天数

    while (scanf("%d%d",&year, &n)) {

        //先判断是否是闰年
        bool isLeap = (year%400 == 0|| (year%100 != 0 && year%4 == 0));//根据是否闰年选择mday的不同行

        if (isLeap) {
                mday[2] = 29;
        }else {
            mday[2] = 28;
        }
        //nextday
        int day = 0,mon = 0;
        for(int i = 0; i < n; i ++) {
            ++day;
            if (day > mday[mon]) {
                day = 1;
                ++mon;
                if (mon > 12) {
                    mon = 1;
                    ++year;
                }
            }
        }
        printf("%04d-%02d-%02d", year, mon, day);
    }
    return 0;
}

例题2.8 日期累加

题目描述:计算一个日期加上若干天后是什么日期。
输入:输入第一行表示样例个数m,接下来的m行中,每行四个整数,分别表示年、月、日和累加天数。
输出:输出m行,每行按照yyyy-mm-dd的格式输出

#include 

using namespace std;
//日期累加
//给定一个日期和天数n, 计算该日期n天后的日期按照yyyy-mm-dd格式输出
bool isLeap(int year) {
        return year%400 == 0 || (year%100 != 0 && year%4 == 0);
    }
int main()
{
    int mday[2][13] = {
    {-1,31,28,31,30,31,30,31,31,30,31,30,31},
    {-1,31,29,31,30,31,30,31,31,30,31,30,31}
    };
    int year, mon, day;
    int n;
    int row;

     row = isLeap(year);
    //int ldaynumber;
    while (scanf("%d%d%d%d",&year, &mon, &day, &n)) {
            int endyear = year, endmon = mon, endday = day;
            //nextday
        for (int i = 0; i < n; ++i) {
            ++endday;
            if (endday > mday[row][mon]) {
                endday = 1;
                ++endmon;
                if (endmon > 12) {
                    endmon = 1;
                    ++endyear;
                    row = isLeap(endyear);
                }
            }
        }
        printf("%04d-%02d-%02d", endyear, endmon, endday);
    }
    return 0;
}

注意:printf输出时,无需加&符号,否则打印的是该变量的地址。

例题2.9 剩下的树

#include 

using namespace std;

//例题2.9 剩下的树
int main()
{
    int tree[10001] = {0};
    int L, M;
    while (scanf("%d%d",&L, &M) != EOF) {
            for (int i = 0; i <= L; ++i) {//种下L+1棵树
                tree[i] = 1;
            }
            while (M--) {//对M组数据进行处理
                int Left, Right;
                scanf("%d%d", &Left, &Right);
            for (int i = Left; i <= Right; ++i) {
                tree[i] = 0;
            }
            }
            int num = 0;//初始化剩下的树的棵数
            for (int i = 0; i <= L; ++i) {
                if (tree[i]) ++num;
            }
            printf("%d", num);
    }

    return 0;
}

例题2.10 手机键盘

题目描述:按手机键盘输入字母的方式,计算所花费的时间 如:a,b,c都在“1”键上,输入a只需要按一次,输入c需要连续按三次。 如果连续两个字符不在同一个按键上,则可直接按,如:ad需要按两下,kz需要按6下 如果连续两字符在同一个按键上,则两个按键之间需要等一段时间,如ac,在按了a之后,需要等一会儿才能按c。 现在假设每按一次需要花费一个时间段,等待时间需要花费两个时间段。 现在给出一串字符,需要计算出它所需要花费的时间。
思路:知道手机九键的排列方式,如图:
计算机考研机试指南第二版(王道)——第二章 暴力求解_第4张图片
需要注意的是7和9上有四个英文字母。据此需要一个keyMap用于指定每个字母所对应的键位,再用一个map用于指定每个字符输入时所花市场。

#include 
#include 
#include 
using namespace std;
//例题2.10手机键盘

int main()
{
    //声明一个字母和数字的对应map
    map <char, int> keyMap  {
    {'a', 2},{'b', 2},{'c', 2},
    {'d', 3},{'e', 3},{'f', 3},
    {'g', 4},{'h', 4},{'i', 4},
    {'j', 5},{'k', 5},{'l', 5},
    {'m',6},{'n',6},{'o',6},
    {'p',7},{'q',7},{'r',7},{'s',7},
    {'t',8},{'u',8},{'v',8},
    {'w',9},{'x',9},{'y',9},{'z',9}
    };
    //声明一个字母在该按键上要按的次数map
    map <char, int> inputTime {
    {'a', 1},{'b', 2},{'c', 3},
    {'d', 1},{'e', 2},{'f', 3},
    {'g', 1},{'h', 2},{'i', 3},
    {'j', 1},{'k', 2},{'l', 3},
    {'m',1},{'n',2},{'o',3},
    {'p',1},{'q',2},{'r',3},{'s',4},
    {'t',1},{'u',2},{'v',3},
    {'w',1},{'x',2},{'y',3},{'z',4}
    };

    char str[101];
    while (scanf("%s",str) != EOF) {
        int lastInput = 1; //lastInput用于判断上一次按键是否与本次按键相同,初始化为1
        int totalTime = 0;
        for (int i = 0; str[i] != '\0'; ++ i) {
                //先计算等待时长
                if (keyMap[str[i]] == lastInput) {//若字符所在键位和上一次键位相同,等待时长+2
                    totalTime += 2;
                }
                //本字符按键所花时间
            totalTime = totalTime + inputTime[str[i]];
            lastInput = keyMap[str[i]];
        }
        printf("%d", totalTime);
    }

    return 0;
}

习题 2.7

**题目描述:给定一个日期,计算给定日期是星期几,按照10 April 2000格式输入 **

#include 
#include 
#include 
#include 
using namespace std;
//例题2.7 DayofWeek
int main()
{
    int year, mon, day;
    char str[100];//C风格只用于scanf和printf
    string month;
    //一年当中1,3,5,7,8,10,12均为31天,4,6,9,11均为30天,2月则根据平年闰年为28或者29天
    int mday[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};
    string intToweek[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
    //由string取到int,即通过月份名称取月份数
    map<string,int> monthToint =
    {
        {"January",1},
        {"February",2},
        {"March",3},
        {"April",4},
        {"May",5},
        {"June",6},
        {"July",7},
        {"August",8},
        {"September",9},
        {"October",10},
        {"November",11},
        {"December",12},
    };


    // 9 October 2001
    //scanf 要使用C风格
    bool isBefore;//判断输入的日期是在当前日期之前还是之后
    while (scanf("%d%s%d",&day, str, &year) != EOF)
    {
        month = str; //把字符串从C风格转换成C++风格
        mon = monthToint[month];//从字符串到整数
        if (year < 2023
            || 2023 == year && mon < 3
            || 2023 == year && 3 == mon && day < 9) {
                isBefore = true;
            }
            else {
                isBefore = false;
            }

            //从begin走到end
            int begYear, begMon,begDay,endYear,endMon,endDay;
            if (isBefore) {//给定日期在之前
                begYear = year;
                begMon = mon;
                begDay = day;
                endYear = 2023;
                endMon = 3;
                endDay = 9;
            } else {
                begYear  = 2023;
                begMon  = 3;
                begDay   = 9;
                endYear = year;
                endMon = mon;
                endDay = day;
            }
            int totalDay = 0;
            while (true) {
                if(begYear == endYear && begMon == endMon && begDay == endDay) {
                    break;
                }
                ++totalDay;
                //nextDay
                bool isLeap = begYear % 400 == 0 || (begYear%100 != 0 && begYear%4 == 0);
                if (isLeap) {
                    mday[2] = 29;
                } else {
                    mday[2] = 28;
                }
                ++begDay;
                if (begDay > mday[begMon]) {
                    begDay = 1;
                    ++begMon;
                    if (begMon > 12) {
                        begMon = 1;
                        ++begYear;
                    }
                }
            }
            if(isBefore) {
                //若在当前日期之前,则(x+totalDay)%7 = 4 -> x = (11-totalDay%7)%7
                printf("%s\n", intToweek[(11-totalDay%7)%7].c_str());
            } else {
                //若在当前日期之后,则有x = (4+totalDay)%7
                printf("%s\n",intToweek[(4+totalDay)%7].c_str());
            }
    }

    return 0;
}

你可能感兴趣的:(王道机试,考研,算法)