кроссворды, задачки, головоломки

Сборник задач разного уровня сложности по математике, информатике, физике, химии, программированию, экономике etc. Логические задачи, SQL задачи, решение задач. Задачи с ответами, а также нерешённые задачи.

Petruchek.Info

Без соседних нулей

Добавлено: 08.02.09 в 12:30
Метки: системы счисления

Требуется вычислить количество N-значных чисел в системе счисления с основанием K, таких что их запись не содержит двух подряд идущих нулей.

Ограничения: 2<=K<=10; 2<=N; 4<=N+K<=180.

Файл исходных данных:
Числа N и K в десятичной записи, разделенные пробелом или переводом строки.

Файл результата:
Программа должна выдать искомое число в десятичной записи.

Пример исходных данных:
2
10

Пример результата:
90

СПРЯТАТЬ РЕШЕНИЕ/ОТВЕТ

/* Авторское решение к областной олимпиаде по информатике
   16 февраля 2002 г. Запорожье.
   (с) Ильяшенко Матвей 2002 г.
*/

#include <stdio.h>
#include <string.h>

#ifndef lenL
  // Размер по умолчанию массива, содержащего длинное число
  #define lenL 45
#endif

/*
*  Функции дублирования содержимого массива длинного числа
*  (содержимое копируется из src в dest)
*    *dest,*src    - указатели на массивы длинных чисел
*/

void cpyL(short *dest,short *src)
{ int i;

  for(i=0;i<lenL;i++)
    dest[i]=src[i];

}

inline void dupL(short *dest,short *src)

{ cpyL(dest,src); }

/*
*  Функция обнуления части элементов массива длинного числа
*    *a        - указатель на массив длинного числа
*    from=0    - первый элемент, подлежащий обнулению
*    to=lenL-1 - последний элемент +1, подлежащий обнулению
*/

void nullL(short *a,int from=0,int to=lenL)

{ int i;
  for(i=from;i<to;i++)

    a[i]=0;
}

/*
*  Функция определения первого значащего разряда длинного числа
*    *a        - указатель на массив длинного числа
*  Возврат:
*    Номер первого значащего разряда
*    Если число равно 0, то возвращается 0
*/

int firstL(short *a)
{ int i;

  for(i=lenL-1;i>=0;i--)

    if(a[i]!=0)
      return(i);

  return(0);
}

/*
*  Функция сравнения двух длинных чисел
*    *a,*b    - указатели на массивы длинных чисел
*  Возврат:
*    1 - если a>b
*    0 - если a=b
*   -1 - если a

int cmpL(short *a,short *b)
{ int i;

  for(i=lenL-1;i>=0;i--)

  { if(a[i]>b[i]) return(1);

    if(a[i]<b[i]) return(-1);

  }
  return(0);
}

/*
*  Функция вывода длинного числа на stdout
*    *a        - указатель на массив длинного числа
*/

void writeL(short *a)
{ int i,first;

  first=firstL(a);
  printf("%d",a[first]);

  for(i=first-1;i>=0;i--)

    printf("%04d",a[i]);
}

/*
*   Функция здвига числа влево
*    *a        - указатель на массив длинного числа
*     n        - количество разрядов, на которое сдвигается число
*/

void shlL(short *a,int n)
{ int i;

  for(i=lenL-1;i>=n;i--)

    a[i]=a[i-n];
  nullL(a,0,n);

}

/*
*  Функция добавления к длинному числу числа типа short
*    *a        - указатель на массив длинного числа
*     n        - добавляемое число (оно же является переполнением)
*  Возврат:
*     0 - не было переполнения
*     1 - было переполнение
*/

int addL(short *a,short n)
{ int i=0;

  long sum;
  do
  { sum=(long)(a[i])+(long)(n);

    a[i]=(short)(sum%10000L);
    n=(short)(sum/10000L);

    i++;
  }while((i!=lenL)&&(n!=0));

  return((int)(n));
}

/*
*  Функция добавления к длинному числу a длинного числа b
*    *a,*b    - указатели на массивы длинных чисел
*  Возврат:
*     0 - не было переполнения
*     1 - было переполнение
*/

int addL(short *a,short *b)
{ int i=0;

  short n=0;
  long sum;
  do

  { sum=(long)(a[i])+(long)(b[i])+(long)(n);

    a[i]=(short)(sum%10000L);
    n=(short)(sum/10000L);

    i++;
  }while(i!=lenL);

  return((int)(n));
}

/*
*  Функция умножения длинного числа на число типа short
*    *a        - указатель на массив длинного числа
*     n        - добавляемое число (оно же является переполнением)
*  Возврат:
*     0 - не было переполнения
*     1 - было переполнение
*/

int mulL(short *a,short n)
{ int i;

  short p=0;
  long mul;
  for(i=0;i<lenL;i++)

  { mul=(long)(a[i])*(long)(n)+(long)(p);

    a[i]=(short)(mul%10000L);
    p=(short)(mul/10000L);

  }
  return((int)(p));
}

/*
*  Функция умножения длинного числа a на длинное число b
*    *a,*b    - указатели на массивы длинных чисел
*  Возврат:
*     0 - не было переполнения
*     1 - было переполнение
*/

int mulL(short *a,short *b)
{ int i,first_b,res=0;

  short t[lenL],r[lenL];
  nullL(r);

  first_b=firstL(b);
  for(i=0;i<=first_b;i++)

    if(b[i]!=0)
    { cpyL(t,a);

      shlL(t,i);
      mulL(t,b[i]);

      if(addL(r,t)) res=1;
    }

  cpyL(a,r);
  return(res);
}

/*
*  Функция возведения числа x в степень y
*    *a - указатель на длинное число, в которое будет записан результат
*     x - основание степени
*     y - показатель степени
*  Возврат:
*     0 - не было переполнения
*     1 - было переполнение
*/

int powL(short *a,int x,int y)

{ int i,res=0;
  nullL(a); addL(a,1);

  for(i=0;i<y;i++)
    res+=mulL(a,x);

  return((int)(res>0));
}

short Table[2][180][lenL];

short Result[90][lenL];

void main()

{ short Sum[lenL],t[lenL],pow[lenL];

  long N,K,i,j,k,Pmax;
  scanf("%ld%ld",&N,&K);

  Pmax=N/2;
  for(j=0;j<=N;j++)

  { nullL(Table[0][j]); addL(Table[0][j],1); }

  nullL(Result[0]); addL(Result[0],1);

  for(i=1;i<=Pmax;i++)
  { for(j=i*2;j<=N;j++)

    { nullL(Table[i%2][j]);

      for(k=(i-1)*2;k<=j-2;k++)

	addL(Table[i%2][j],Table[(i-1)%2][k]);

    }
    dupL(Result[i],Table[i%2][N]);

  }
  nullL(Sum);
  powL(pow,K-1,N-Pmax-1);

  for(i=0;i<=Pmax;i++)
  { mulL(pow,K-1);

    dupL(t,pow);
    mulL(t,Result[Pmax-i]);

    addL(Sum,t);
  }
  writeL(Sum);

  printf("\n");
}
источник: Запорожская областная олимпиада по информатике, 2002

Комментарии
Google says:
Manny Calavera (09.11.09):
На мой взгляд, далеко не самое оптимальное решение. Нет необходимости в столь громоздких вычислениях и возведении в степень тем более. Используем индукцию. Пусть xN - число N-значных чисел в системе счисления с основанием K, которые оканчиваются на 0, а yN - число таких чисел, которые не оканчиваются на 0.
Тогда x(N+1) = yN, а y(N+1) = (xN+yN)*(K-1). База индукции - x1 = 0, y1 = K-1. Ответом будет xN+yN. Если даже взять авторские "длинные числа", то main будет как минимум использовать меньше памяти и меньше операций( не ~N^2, а ~N )

void main(){
short x[lenL];
short y[lenL];
short t[lenL];
int N;
int K;
int i;
scanf("%d%d", &N, &K );
{nullL(x);addL(x,(int)0);}
{nullL(y);addL(y,K-1);}
for( i = 1; i < N; i++ ){
dupL(t,x);
dupL(x,y);
addL(y,t);
mulL(y,K-1);
}
addL(y,x);
writeL(y);
printf("\n");
}
Nodirbek (06.02.11):
Задача на динамическое программирование ;)
AlexeyK (03.02.12):
Поправка к решению Manny Calavera: База индукции - x1=1, y1=K-1 это не простя формальность - например если K=8, N=10 мы получим: 837677687 при x1=0 и 943881120 при x1=1 - разница в 106203433 !
Комментарий от новенького:
Новенький является
Новенький не робот
Знаки на картинке: латинские буквы, арабские цифры


Есть на сайте: Онлайн кроссворды Задачи Онлайн игры Блог
Все работы, опубликованные на сайте — авторские, если не указано иное. Перепечатка возможна только с письменного разрешения владельцев ресурса, с обязательной ссылкой на сайт petruchek.info. Пишите нам: . Сайт должен работать в IE, FF, Opera, Safari.

Реклама:

Разработано в студии "Webous"о проектесайта карта

Реклама: