Реализация числа нарцисса на основе C++

1. Компания с количеством нарциссов

1.1.Количество нарциссов

Проходя курсы программирования, большинство читателей, должно быть, написали программу для решения числа нарцисса, используя структуру цикла.
[Пример 1-1] Число Нарцисса
— это трехзначное целое число (100~999). Если сумма кубов каждой цифры равна самому числу, оно называется «числом нарцисса» (например: 153=13). +53+33) , найдите все такие числа.
Идеи программирования
: Выполните исчерпывающий перебор трехзначных чисел n (n — целое число от 100 до 999). Для каждого перечисленного n разложите его цифру сотен a (a=n/100), цифру десятков b (b=n%100/10) и цифру единиц c (c=n%10). Если a a выполнено a + b b b+c c c== n, тогда n — число Нарцисса.
Исходная программа и текущие результаты

# include <iostream>
using namespace std;
int main()

{
    
    

   int n, a, b, c;    //n、a、b和c分别为三位数自身及其百位、十位和个位

   for(n=100 ;n<=999;n++)     

     {
    
    

        a=n/100;

        b=n%100/10;

        c=n%10;      

     if(a*a*a+b*b*b+c*c*c== n)

       cout<<n<<" ";

     }

   cout<<endl;

   return 0;  

}

Скомпилируйте и запустите приведенную выше программу, чтобы получить результаты, показанные ниже.

153 370 371 407
Press any key to continue

1.2.Первоначальный лагерь компании Narcissus Numbers

В игре «Троецарствие» у военного генерала Лу Сюня есть навык под названием «Ляньин».Описание навыка следующее: всякий раз, когда вы теряете последнюю карту на руке, вы можете немедленно вытянуть карту. Заимствуя это понятие, в практике программирования после того, как мы спроектировали программу, мы можем на основе этой программы расширить ее, задать еще один аналогичный вопрос, а затем спроектировать ее. То есть после решения одной задачи решите другую задачу.Объяснение аналогии — вытянуть еще одну карту после потери карты на руке. Опираясь на предыдущий вопрос о количестве нарциссов, мы можем через лагерь компании задать следующие вопросы.
[Пример 1-2] Четырехзначное число цветка представляет собой
четырехзначное целое число (1000~9999). Если сумма 4-й степени каждой цифры равна самому числу, оно называется четырехзначным числом цветка. (например: 1634=14+64+34+44), найдите все такие числа.
Идеи программирования
Идеи программирования полностью аналогичны числу нарцисса. То есть выполнить исчерпывающий перебор четырехзначных чисел n (n — целое число от 1000 до 9999). Для каждого перечисляемого n разложите его цифру тысяч a (a=n/1000), цифру сотен b (b=n%1000/100), цифру десятков c (b=n%100/10) и цифру единиц d (d= n%10), если он удовлетворяет условию a a a a + b b b b+cc c c c+d d d d== n, то n — 4-значный номер цветка.
Исходная программа и текущие результаты

#include <iostream>
using namespace std;

int main()
{
    
    

   int n, a, b, c,d; //n、a、b、c和d分别为四位数自身及其千位、百位、十位和个位

   for(n=1000 ;n<=9999;n++)     

     {
    
    

     a=n/1000;

        b=n%1000/100;

        c=n%100/10;

        d=n%10;      

     if(a*a*a*a+b*b*b*b+c*c*c*c+d*d*d*d==n)

       cout<<n<<" ";

     }

   cout<<endl;

   return 0;  

}

Скомпилируйте и запустите приведенную выше программу, чтобы получить результаты, показанные ниже.

1634 8208 9474
Press any key to continue

[Пример 1-3] Пятизначное число цветка представляет собой
пятизначное целое число (10000~99999). Если сумма пятой степени каждой цифры равна самому числу, оно называется пятизначным числом цветка. (например: 54748=55+ 45+75+45+85), найдите все такие числа.
Идеи для программирования
Все еще можно найти все пятизначные числа цветов, используя метод решения, который полностью аналогичен числу нарцисса. То есть исчерпывающее перечисление 5-значного числа n (n — целое число от 10000 до 99999). Для каждого перечисляемого n разложите его цифру тысяч a (a=n/10000), цифру тысяч b (b=n%10000/1000), цифру сотен c (c=n%1000/100) и цифру десятков d (d =n%100/10) и цифра единиц e (e=n%10), если она удовлетворяет условиям a a a a a + b b b b b b +cc c c c c+d d d d d+e e e e e= = n, тогда n — пятизначный номер цветка.
Исходная программа и текущие результаты

#include <iostream>
using namespace std;

int main()

{
    
    

   int n, a, b, c,d,e; //n、a、b、c和d分别为5位数自身及其万、千、百、十和个位

   for(n=10000 ;n<=99999;n++)     

     {
    
    

     a=n/10000;

        b=n%10000/1000;

        c=n%1000/100;

        d=n%100/10;

        e=n%10;

     if(a*a*a*a*a+b*b*b*b*b+c*c*c*c*c+d*d*d*d*d+e*e*e*e*e==n)

       cout<<n<<" ";

     }

   cout<<endl;

   return 0;  

}

Скомпилируйте и запустите приведенную выше программу, чтобы получить результаты, показанные ниже.

54748 92727 93084

Press any key to continue

Следуя методам примеров 1–2 и 1–3, мы можем найти все 6-значные номера цветов, 7-значные номера цветов и так далее. Однако согласно описанному выше методу вам необходимо модифицировать программу, добавить в программу переменные и операторы соответственно. Можете ли вы ввести натуральное число N (для простоты N<=9, поскольку максимальное значение 32-битного целого числа в C++ равно 2147483647, а большее количество цифр выйдет за пределы табличного диапазона данных типа int) и найти все N цифры А как насчет количества цветов?

1.3.Дальнейшее расширение численности Нарциссов.

[Пример 1-4] N-значное число цветов
N-значное положительное десятичное целое число, если сумма N-й степени чисел в каждой цифре равна самому числу, оно называется N-значным числом цветка.
Например: когда N=3, 153 удовлетворяет условию. Когда N=4, 1634 удовлетворяет условию. Когда N=5, 54748 соответствует условию.
Фактически, для каждого значения N может существовать несколько чисел, удовлетворяющих условию. Напишите программу, которая найдет количество всех N-значных цветов для входного сигнала N (N<=9).
Идея программирования
Чтобы решить N-значное число цветов, необходимо полностью пересчитать N-значное число x (x — целое число от 10N-1 до 10N-1). Для каждого перечисленного x найдите сумму цифр N-й степени чисел, стоящих на каждой цифре. Если digitsum==x удовлетворяется, то x — N-значное цветочное число.
Чтобы решить N-значное количество цветов, можно абстрагировать две функции. Один из них — int power(int x,int n), который используется для нахождения n-й степени x; другой — int digitsum(int x,int n), который используется для нахождения суммы n-й степени каждой цифры. из х.
Исходная программа и текущие результаты

# include <iostream>

# include <ctime>

using namespace std;

int power(int x,int n)

{
    
    

    int i,p=1;

    for (i=1;i<=n;i++)

       p*=x;

    return p;

}

int digitsum(int x,int n)

{
    
    

    int s=0;

    while(x!=0)

    {
    
    

       s+=power(x%10,n);

       x=x/10;

    }

    return s;

}

int main()

{
    
    

  int n, x,a, b;    //a、b分别为相应穷举区间的上下界限

  cin>>n;

    a=power(10,n-1);

    b=power(10,n)-1;

    int start=clock();

    for(x=a; x<=b; x++)     

    {
    
    

     if(digitsum(x,n)==x)

       cout<<x<<" ";

    }

   cout<<endl<<"Running Time :"<<clock()-start<<" ms."<<endl;

  return 0;  

}

Скомпилируйте и запустите приведенную выше программу. Если вы введете 5, вы получите результаты, показанные ниже.

5

54748 92727 93084

Running Time :16 ms.

Press any key to continue

Если вы введете 7, вы получите результаты, показанные ниже.

7

1741725 4210818 9800817 9926315

Running Time :3234 ms.

Press any key to continue

Если вы введете 8, вы получите результаты, показанные ниже.

8

24678050 24678051 88593477

Running Time :39282 ms.

Press any key to continue

Если вы введете 9, вы получите результаты, показанные ниже.

9

146511208 472335975 534494836 912985153

Running Time :457875 ms.

Press any key to continue

Приведенная выше программа может вычислить количество всех N-значных цветов на основе входных данных N. Однако из результатов выполнения видно, что для поиска 7-значного номера цветка требуется 3234 мс, что намного дольше, чем 16 мс для поиска 5-значного номера цветка. Чтобы найти количество 9-значных цветов, требуется 457875 мс. Можете ли вы придумать какие-нибудь способы решить эту проблему быстрее?
Фактически, когда мы решаем проблемы, когда объем данных относительно невелик, не имеет значения, какой метод мы используем, потому что обычно мы можем быстро получить результаты; но когда объем данных относительно велик, нам необходимо оптимизировать метод решения проблемы. В противном случае затраты времени неприемлемы.

2. Собирание мудрости по количеству цветов

2.1.Предварительный сбор знаний о количестве цветков.

В игре «Три королевства» у генерала Хуан Юэина есть навык под названием «Сбор мудрости».Описание навыка следующее: Каждый раз, когда вы используете мешок с чаевыми без задержки (до того, как он будет решен), вы можете немедленно вытянуть карту. То есть, когда Хуан Юэин разыгрывает такие карты в игре, как создание чего-то из ничего, кража овец, пересечение рек и сжигание мостов, убийство людей одолженными мечами, небывалый урожай, вторжение южных варваров, тысячи стрел, дуэль, персик. фруктовый сад, клятва дружбы, неуязвимость, железное звено цепи и т. д., вы можете вытянуть еще одну карту.
Заимствуя эту концепцию в практике программирования, после того как мы спроектируем программу, мы можем затем оптимизировать и расширить ее на основе этой программы, чтобы посмотреть, сможем ли мы использовать другие, более лучшие методы для решения этой проблемы. То есть после использования одного метода решения проблемы используйте другой метод решения проблемы, то есть после разыгрывания карты без задержки вытяните другую карту (чтобы посмотреть, сможете ли вы найти лучшее решение).
Ниже представлена ​​предварительная оптимизация программы в примерах 1-4.
Из данной программы видно, что для каждого бита каждого перечислимого числа x (x%10) в программе будет вызываться степенная функция для нахождения его n-й степени. Например, если входное число равно 5, то будет перечислено 90 000 5-значных чисел (10 000 ~ 99 999), каждое число имеет 5 цифр, а степенная функция будет вызываться 450 000 раз.
Фактически, каждый вызов власти — это не что иное, как поиск n-й степени числа среди 10 чисел от 0 до 9. Следовательно, если функция степени вызывается заранее, чтобы узнать n-ю степень чисел 0~9 и сохранить ее в массиве, то при нахождении суммы n-й степени каждой цифры x вам нужно использовать только соответствующее значение. в массиве, без повторного вызова функции степени. Это определенно сократит время решения.
Исходная программа, оптимизированная с использованием этой идеи, выглядит следующим образом:

#include <iostream>
#include <ctime>

using namespace std;

int table[10];

int power(int x,int n)

{
    
    

    int i,p=1;

    for (i=1;i<=n;i++)

       p*=x;

    return p;

}

int digitsum(int x,int n)

{
    
    

    int s=0;

    while(x!=0)

    {
    
    

       s+=table[x%10]; // 直接引用表中预先求得的值

       x=x/10;

    }

    return s;

}

int main()

{
    
    

  int n, x,a, b;    //a、b分别为相应穷举区间的上下界限

  cin>>n;

    a=power(10,n-1);

    b=power(10,n)-1;

    int start=clock();

  for (x=0; x<=9; x++) // 初始化数据表

       table[x]=power(x,n);

    for(x=a; x<=b; x++)     

    {
    
    

     if(digitsum(x,n)==x)

       cout<<x<<" ";

    }

   cout<<endl<<"Running Time :"<<clock()-start<<" ms."<<endl;

  return 0;   

}

Скомпилируйте и запустите приведенную выше программу. Если вы введете 7, вы получите результаты, показанные ниже.

7

1741725 4210818 9800817 9926315

Running Time :984 ms.

Press any key to continue

Если вы введете 8, вы получите результаты, показанные ниже.

8

24678050 24678051 88593477

Running Time :11125 ms.

Press any key to continue

По результатам выполнения видно, что после использования табличного метода время решения существенно сокращается.

2.2.Дальнейший сбор знаний о количестве цветов.

Хотя эффективность вышеуказанной программы повысилась, она все еще не идеальна. Например, если вы запустите приведенную выше программу и введете 9, вы получите следующие результаты.

9

146511208 472335975 534494836 912985153

Running Time :125563 ms.

Press any key to continue

Из результатов прогона видно, что поиск 9-значного числа цветов занимает около 2 минут. Давайте поищем лучший способ решения задачи нахождения количества N-значных цветов.
В примере 1-3, чтобы найти количество 5-значных цветов, необходимо тщательно пересчитать все 90 000 5-значных чисел (10 000 ~ 99 999), чтобы увидеть, равна ли сумма 5-й степени каждой цифры каждого числа этому числу. . Фактически, вы можете перечислить количество вхождений 10 чисел от 0 до 9 (каждое число может встречаться от 0 до 5 раз).Когда сумма вхождений всех чисел равна 5, это означает, что комбинация чисел может встречаться от 0 до 5 раз. быть 5. количество цветов, а затем найдите сумму 5-й степени каждого числа, умноженную на количество раз, которое оно встречается, а затем определите, совпадает ли количество раз, когда каждое число появляется в сумме, с количеством раз каждое число появляется при перечислении всех чисел. , если они одинаковы, сумма представляет собой 5-значное цветочное число.
Например, когда число 2 появляется 2 раза, 7 появляется 2 раза, а 9 появляется 1 раз, поскольку 2+2+1=5, сумма вычислений = 2 25 +2 75+1*95=64+ 33614+59049. =92727.Проверьте каждую цифру полученной суммы.Если выпадет ровно 2 двойки, 27 и 19, то это означает, что данная комбинация цифр образует сумму 5-значного цветочного числа.
Согласно этой идее истощения, необходимо разобраться только с ситуациями 2002 года. Это связано с тем, что новая идея заключается в подсчете количества раз появления числа. Всего существует 7 ситуаций:

  1. Бывает ситуация, когда каждое число в 5-значном числе встречается только один раз (т.е. 1+1+1+1+1).
  2. В пятизначном числе возникает ситуация, когда одна цифра появляется дважды, а остальные три цифры появляются по одному разу (т. е. 1+1+1+2).
  3. Имеется ситуация 1+2+2 (то есть одно число появляется один раз, одно число появляется два раза, а другое число появляется два раза).
  4. Имеется ситуация 1+1+3 (то есть одно число появляется один раз, другое число появляется один раз, а третье число появляется три раза).
  5. Имеется ситуация 1+4 (то есть одно число появляется 1 раз, а другое 4 раза).
  6. Имеется ситуация 2+3 (то есть одно число встречается 2 раза, а другое 3 раза).
  7. Одно число появляется 5 раз, а ситуаций 10.

252+840+360+360+90+90+10=2002.
Поэтому ключом к решению проблемы согласно новым идеям является исчерпывающий подсчет количества вхождений каждого числа.
[Пример 1-5] Числовая комбинация
Всего существует 10 чисел от 0 до 9. Теперь необходимо взять n чисел из этих 10 чисел, чтобы сформировать числовую комбинацию. Каждое число можно брать повторно, но порядок числа не учитываются.
Например, когда n=3, 123, 132, 213, 231, 312 и 321 считаются одной и той же комбинацией чисел (по одному из чисел 1, 2 и 3), а 112 (две единицы и двойка) и 122 (одна 1 и две 2) — это разные комбинации цифр.
Напишите программу, введите n и выведите количество всех комбинаций. Например, вход 3 и выход 220; вход 5 и выход 2002; вход 8 и выход 24310.
Идея программирования 1.
Определите массив дублей для сохранения количества вхождений каждого числа в n цифрах. Самый прямой способ — написать 9-кратный цикл: номер внешнего цикла — от 0 до n для назначения take[0], а номер второго внутреннего цикла — от 0 до n-take[0] для take[2]. .] присваивание,... и т. д., самый внутренний номер цикла от 0 до используется для присвоения значения take[8], а остальные значения присваиваются take[9].
Исходная программа 1

#include <iostream>

using namespace std;

int main()

{
    
    

    int n,i,cnt=0,take[10]={
    
    0};

    cin>>n;

    for (take[0]=0; take[0]<=n; take[0]++)

    {
    
    

     for (take[1]=0; take[1]<=n- take[0]; take[1]++)

     {
    
    

      for (take[2]=0; take[2]<=n- take[1]- take[0]; take[2]++)

       {
    
    

       for (take[3]=0; take[3]<=n- take[2]- take[1]- take[0]; take[3]++)

        {
    
    

        for (take[4]=0; take[4]<=n- take[3]- take[2]- take[1]- take[0]; take[4]++)

        {
    
    

         for (take[5]=0; take[5]<=n- take[4]- take[3]- take[2]- take[1]- take[0]; take[5]++)

         {
    
    

          for (take[6]=0; take[6]<=n- take[5]- take[4]- take[3]- take[2]- take[1]- take[0]; take[6]++)

          {
    
    

           for (take[7]=0; take[7]<=n- take[6]- take[5]- take[4]- take[3]- take[2]- take[1]- take[0]; take[7]++)

           {
    
    

            for (take[8]=0; take[8]<=n- take[7]- take[6]- take[5]- take[4]- take[3]- take[2]- take[1]- take[0]; take[8]++)

            {
    
    

                    take[9]=n- take[8]- take[7]- take[6]- take[5]- take[4]- take[3]- take[2]- take[1]- take[0];

                    for (i=0;i<=9;i++)

                       cout<<"i:"<<take[i]<<" ";

                    cout<<endl;

                    cnt++;

               }

            }

          }

        }

       }

      }

     }

     }

    }

    cout<<"Count="<<cnt<<endl;

    return 0;

}

Идея программирования 2.
Хотя приведенная выше идея проста, метод записи 9-кратного цикла слишком громоздкий. Фактически, если массиву take присваиваются значения из take[0]~take[9], чтобы увидеть процесс последовательного присваивания, то этот процесс можно абстрагировать в рекурсивный процесс.
Пусть функция DFS(int take[],int index,int Leave) означает присвоение каждого значения между 0~leave элементу массива take[index], а затем присвоение 0~leave элементу массива take[index+1]. - Для каждого значения между take[index] рекурсивно вызывается функция DFS(take[], index+1, left- take[index]). Условием завершения рекурсии является index==10, то есть все значения take[0]~take[9] являются присвоенными. Первоначальный вызов рекурсии — index=0, Leave=n.
Исходная программа 2 и текущие результаты

#include <iostream>

using namespace std;

int cnt=0;

void DFS(int take[],int index,int leave)

{
    
    

  int i;

    if(index==9)       // 0~8各数字使用个数列举完成

    {
    
    

    take[index]=leave;  // 剩余的给数字9

       cnt++;       // 有效取数组合,计数

       return ;

    }

    for(i=0;i<=leave;i++)

    {
    
    

       take[index]=i;

       DFS(take,index+1,leave-i);

    }

}

int main()

{
    
    

    int n, take[10]={
    
    0};

    cin>>n;

    DFS(take,0,n);

    cout<<"Count="<<cnt<<endl;

    return 0;

}

Скомпилируйте и запустите приведенную выше программу. Если вы введете 5, вы получите результаты, показанные ниже.

5

Count=2002

Press any key to continue

С помощью приведенной выше функции комбинирования чисел void DFS (int take[], int index, int Leave), после сохранения количества вхождений каждого числа от 0 до 9 в n-значном числе в массиве take, цикл может использоваться

sum=0;

for (i=0;i<10;i++)

sum=sum+take[i]*power(i,lenN);

Вычислите сумму степеней lenN каждой цифры в текущей комбинации. Затем используйте функцию Судья, чтобы определить, совпадает ли комбинация чисел, появляющихся в сумме, с комбинацией в массиве дублей. Если они одинаковы, это количество n-значных цветов.
Исходная программа, оптимизированная с использованием новых идей, выглядит следующим образом:

#include <iostream>
#include <ctime>

using namespace std;

int table[10];

int power(int num,int n)

{
    
    

    int p=1,i;

  for (i=1;i<=n;i++)

       p=p*num;

    return p;

}

bool Judge(int take[], int sum)

{
    
    

    int i,a[10]={
    
    0};

    while (sum)

    {
    
      

       a[sum%10]++;

       sum/=10;

    }

    for(i=0; i<10 && a[i]==take[i];i++);

    return i==10;

}

void DFS(int take[],int index,int leave)

{
    
    

  int i,sum;

    if(index==9)       // 0~8各数字使用个数列举完成

    {
    
    

    take[index]=leave;  // 剩余的给数字9

    sum=0;

       for (i=0;i<10;i++)

           sum=sum+take[i]*table[i];

       if(Judge(take,sum))

       {
    
    

           cout<<sum<<" ";

       }  

       return ;

    }

    for(i=0;i<=leave;i++)

    {
    
    

       take[index]=i;

       DFS(take,index+1,leave-i);

    }

}

int main()

{
    
    

    int n,i,take[10]={
    
    0};

    cin>>n;

    int start=clock();

  for (i=0; i<=9; i++)  // 初始化数据表

       table[i]=power(i,n);

    DFS(take,0,n); 

    cout<<endl<<"Running Time :"<<clock()-start<<" ms."<<endl;

    return 0;

}

Скомпилируйте и запустите приведенную выше программу. Если вы введете 9, вы получите результаты, показанные ниже.

9

534494836 472335975 912985153 146511208

Running Time :15 ms.

Press any key to continue

Из этого результата выполнения мы видим, что эффективность выполнения программы, оптимизированной с использованием новой идеи, намного выше.

На самом деле мы можем оптимизировать дальше. В качестве примера возьмем 5-значный номер цветка. Поскольку 9 в пятой степени равно 59049, 9 может встречаться в 5-значном номере цветка не более одного раза. В противном случае значение суммы превысит 5 цифр. Следовательно, значение суммы может превышать 5 цифр.Отсечение выполняется на основе количества чисел;кроме того, при использовании слишком большого количества маленьких чисел количество цифр в сумме может быть не достигнуто.Например, 4 в 5-й степени равно 1024, а сумма 5 4, возведенная в 5-ю степень, равна всего 5120. , поэтому, если 5-значное число состоит из чисел ниже 5, сумма 5-й степени не может достигать 5 цифр, вы можете напрямую выполнить следующий поиск и увеличение количества больших чисел. Экспериментальная проверка показывает, что после двух сокращений количество обработанных ситуаций составит 1329, что значительно уменьшится. Более того, чем больше количество битов, тем выше эффективность после использования двух сокращений. Например, при решении 8-значного числа цветка существует 24 310 возможных комбинаций 8-значных чисел от 0 до 9. После обрезки количество возможных комбинаций составляет 16 131.
Для обработки сокращения после того, как текущий номерной индекс определяет число i, i*indexn накапливается в сумме суммы. Для этой цели вы можете рассмотреть возможность добавления параметра sum в функцию DFS, чтобы сохранить текущую сумму. Таким образом, на это значение суммы можно будет ссылаться во время обработки сокращения.
Исходная программа после обрезки выглядит следующим образом:

#include <iostream>
#include <ctime>

using namespace std;

int table[12]; // 其中table[10]保存power(10,n),table[11]保存power(10,n-1)

int power(int num,int n)

{
    
    

    int p=1,i;

  for (i=1;i<=n;i++)

       p=p*num;

    return p;

}

bool Judge(int take[], int sum)

{
    
    

    int i,a[10]={
    
    0};

    while (sum)

    {
    
      

       a[sum%10]++;

       sum/=10;

    }

    for(i=0; i<10 && a[i]==take[i];i++);

    return i==10;

}

void DFS(int take[],int index,int leave,int sum)

{
    
    

  int i,tmp,tmp1;

    if(index==10)       // 0~9各数字使用个数列举完成

    {
    
    

       if(leave>0) return; // 位数不足,直接返回

       if(Judge(take,sum))

       {
    
    

           cout<<sum<<" ";

       }  

       return ;

    }

    for(i=0;i<=leave;i++)

    {
    
    

       take[index]=i;

       tmp=sum+i*table[index];

       if (tmp>=table[10]) break;  // 剪枝1,和值已超过power(10,n)

       tmp1=tmp+(leave-i)*table[9];  // 剩余的数字全用9的话

       if(tmp1<table[11]) continue; // 剪枝2,小数字太多,和值不可能达到位数

       DFS(take,index+1,leave-i,tmp);

    }

}

int main()

{
    
    

    int n,i,take[10]={
    
    0};

    cin>>n;

    int start=clock();

  for (i=0; i<=9; i++)  // 初始化数据表

       table[i]=power(i,n);

    table[10]=power(10,n);

    table[11]=power(10,n-1);

    DFS(take,0,n,0); 

    cout<<endl<<"Running Time :"<<clock()-start<<" ms."<<endl;

    return 0;

}

3. 21-значный номер цветка

На основе предыдущей программы оптимизации давайте решим конкурсный вопрос в конкурсе по разработке программного обеспечения «Blue Bridge Cup». Это большой Босс!
[Пример 1-6] 21-значный номер цветка
Напишите программу для вывода всех 21-значных номеров цветов. (Примечание: это целое число имеет 21 цифру, а сумма его цифр в 21-й степени в точности равна самому числу.)
Идеи программирования
Согласно идее программы оптимизации обрезки, приведенной выше, 21-значное число цветка может быть найденным. Поскольку 21-битные целые числа выходят за пределы диапазона длинных целых чисел, для обработки используются большие целые числа. Определите структуру BigNum следующим образом:

struct BigNum
{
    
    
    int dig[4];
    int len;
};

Среди них элементы массива dig[0]~dig[3] соответственно хранят числа от младшей до старшей цифры большого целого числа.Для экономии места каждый элемент массива хранит 8 цифр большого целого числа. То есть при хранении больших целых чисел существует одна группа из 8 бит от младшего к старшему, и каждая группа сохраняется в элементе массива. Len представляет количество групп на 8-битную группу, что означает количество цифр в большом целом числе (но в качестве единицы измерения используются 8 бит).
На основе больших целых чисел напишите следующие 6 функций для обработки больших целых чисел.

void Init(BigNum &a)// 大整数a初始化为0
void PrintBigNum(BigNum a)// 输出大整数 a
BigNum CarryUp(BigNum a)// 大整数 a 的处理进位
BigNum Multi(BigNum a,int n)// 大整数 a 和整数 n 相乘
int Cmp(BigNum a,BigNum b)// 大整数a和b比较大小
BigNum Add(BigNum a,BigNum b)// 大整数a 和 b 相加

Кроме того, нахождение 21-й степени числа занимает много времени. Чтобы сократить время работы программы, вы можете сначала вычислить значения 0 ~ 9, возведенные в 21-ю степень, и различные времена их появления, и сохранить их в указанном массиве.
Определите массив BigNum pow[10];, где pow[i] (0<=i<=9) хранит 21-ю степень числа i.
Определите массив BigNum sp[10][22]; , где sp[i][j] хранит значение числа i, возведенного в 21-ю степень, умноженного на j (количество вхождений).
Таким образом, когда эти значения необходимы в программе, можно напрямую ссылаться на значения соответствующих элементов массива, так что достаточно будет вычислить только до 10 последовательных сложений больших чисел (подумайте, почему?), без необходимости вычисления возведения в степень и умножения, что существенно экономит время.
Исходная программа и текущие результаты

#include <iostream>
#include <ctime>

using namespace std;

const int BIT=100000000; // 每8位一组

struct BigNum

{
    
    

    int dig[4];

    int len;

};

BigNum pow[10],MAX,MIN;

BigNum sp[10][22];

int take[10]={
    
    0};

int LEN=21;

void Init(BigNum &a)

{
    
    

    a.len=1;

    for (int i=0;i<4;i++)

       a.dig[i]=0;

}

void PrintBigNum(BigNum a)

{
    
    

    int i;

    cout<<a.dig[a.len-1];

    for(i=a.len-2; i>=0; i--)

    {
    
    

       cout.fill('0');    // 定义填充字符'0'

       cout.width(8); cout<<a.dig[i];

    }

    cout<<endl;

}

BigNum CarryUp(BigNum a)  // 处理进位

{
    
    

    int i;

    for(i=0;i<a.len;i++)

    {
    
    

       a.dig[i+1]+=a.dig[i]/BIT;

       a.dig[i]%=BIT;

    }

    return a;

}

BigNum Multi(BigNum a,int n)

{
    
    

    BigNum c;

    int i;

    Init(c);

    c.len=a.len+1;

    for(i=0;i<a.len;i++)

    {
    
    

      c.dig[i]=(a.dig[i])*n;

    }

    c=CarryUp(c);

    if(c.len>0 && c.dig[c.len-1]==0)c.len--;

    return c;

}

int Cmp(BigNum a,BigNum b)

{
    
    

    if(a.len>b.len) return 1;

    if(a.len<b.len) return -1;

    int i;

    for(i=a.len-1;i>=0 && a.dig[i]==b.dig[i];i--);

    if(i==-1) return 0;

    return a.dig[i]-b.dig[i];

}

BigNum Add(BigNum a,BigNum b)

{
    
    

    int i;

    if(b.len>a.len) a.len=b.len;

    for(i=0;i<a.len;i++)

    {
    
    

       a.dig[i]+=b.dig[i];

    }

    a=CarryUp(a);

    if(a.dig[a.len]) a.len++;

    return a;

}

bool Judge(BigNum sum)

{
    
    

    int aa[10]={
    
    0};

    int i,j;

    for(i=1;i<=8;i++)  // 求 sum 中0~9各个数字出现的次数,保存到数组aa中   

       for (j=0;j<3; j++)

       {
    
    

           aa[sum.dig[j]%10]++;

         sum.dig[j]/=10;

       }

    aa[0]=aa[0]-3;

    for(i=0; i<10 && aa[i]==take[i];i++);

    return i==10;

}

void DFS(int deep,BigNum Sum,int leave)

{
    
    

    BigNum check;

    BigNum cc;

    int i;

    if(deep==10)

    {
    
    

       if(leave>0)return;

       if(Judge(Sum))

       {
    
    

           PrintBigNum(Sum);

       }

       return ;

    }

    for(i=0;i<=leave;i++)

    {
    
    

       take[deep]=i;

       check=Add(Sum,sp[deep][i]);

       if(Cmp(check,MAX)>=0) break; // 剪枝1

       cc=Add(check,sp[9][leave-i]);

       if(Cmp(cc,MIN)<0) continue;  // 剪枝2

       DFS(deep+1,check,leave-i);

    }

}

int main()

{
    
    

    int start=clock(); 

    int i, j;

    BigNum sum;

    Init(pow[0]);

    for(i=1;i<10;i++)

    {
    
    

       Init(pow[i]); pow[i].dig[0]=1;

       for (j=1;j<=21;j++)

           pow[i]=Multi(pow[i],i);

    }

    for(i=0;i<10;i++)

       Init(sp[i][0]);

    for(j=0;j<10;j++)

     for(i=1;i<22;i++)

     {
    
    

        sp[j][i]=Add(sp[j][i-1],pow[j]);

     }

  Init(sum);

    MAX.dig[2]=100000;  MAX.len=3;

    MIN.dig[2]=10000;   MIN.len=3;

    DFS(0,sum,LEN);

    cout<<endl<<"Running Time :"<<clock()-start<<" ms."<<endl;

    return 0;

}

Скомпилируйте и запустите приведенную выше программу, чтобы получить результаты, показанные ниже.

128468643043731391252

449177399146038697307

 

Running Time :32046 ms.

Press any key to continue

Занимаясь программированием, надо не просто говорить на тему, а следует пробегаться по «лагерю компании» и «набиранию мудрости». В этом практическом процессе есть несколько звеньев, таких как постановка проблем, решение проблем, расширение проблем, решение проблем, оценка методов решения проблем, оптимизация проектов и т. д. На самом деле это процесс спирали. продвигаясь вперед, он может естественным образом вызвать учебный интерес у учащихся, изучающих программирование, и за счет постоянного расширения «связей» вопросов может эффективно расширить мышление читателей. Этот метод обучения разработке программ посредством поэтапного продвижения программы и непрерывного обучения, по сути, представляет собой поэтапный и спиральный процесс, который позволяет учащимся приступить к программированию в процессе «подъёма по ступенькам». .

Guess you like

Origin blog.csdn.net/s1t16/article/details/134640715
C++