Как решать задачу на счастливые билеты?

Имеется лента билетов с восьмизначными номерами. Первый билет имеет номер M, последний – N. Величины M и N отвечают следующему соотношению: 10000000 ≤ M < N ≤ 99999999. Необходимо определить количество "счастливых" билетов в ленте. Билет считается "счастливым", если сумма первых четырех цифр равна сумме последних четырех цифр.

Помогите с составлением алгоритма.

Смог только для всех возможных случаев, без учета M и N:
int C[37];
	int T, Count = 0;
	for (T = 0; T < 37; ++T)
		C[T] = 0;

	
	for (int a1 = 0; a1 <= 9; ++a1)
		for (int a2 = 0; a2 <= 9; ++a2)
			for (int a3 = 0; a3 <= 9; ++a3) {
				for (int a4 = 0; a4 <= 9; ++a4) {
					T = a1 + a2 + a3 + a4;
					C[T]++;
				}
			}
	
	for (T = 0; T < 37; ++T) {
		//printf("%d\n", C[T]);
		Count += (C[T] * C[T]);
	}

	printf("%d\n", Count);
  • Вопрос задан
  • 6281 просмотр
Решения вопроса 1
Mrrl
@Mrrl
Заводчик кардиганов
Как-то так:

int S0[37],S1[37],S2[37],S3[37],S4[37];

void Expand(int *a,int *b,int n){
	int s0=0;
	for(int i=0;i<n+10;i++){
		if(i<n) s0+=a[i];
		if(i>=10) s0-=a[i-10];
		b[i]=s0;
	}
}

int Conv(int *S,int balance,int m){
	int res=0;
	while(--m>=0) res+=S[m]*S4[balance+m];
	return res;
}

// number of tickets in 0..u-1
int NHappy(int u){
	int res=0;
	int balance=0;
	int digit;

	digit=u/10000000; u%=10000000;
	for(int i=0;i<digit;i++) res+=Conv(S3,balance++,28);
	digit=u/1000000; u%=1000000;
	for(int i=0;i<digit;i++) res+=Conv(S2,balance++,19);
	digit=u/100000; u%=100000;
	for(int i=0;i<digit;i++) res+=Conv(S1,balance++,10);
	digit=u/10000; u%=10000;
	for(int i=0;i<digit;i++) res+=S4[balance++];
	digit=u/1000; u%=1000;
	for(int i=0;i<digit && balance>=0;i++) res+=S3[balance--];
	digit=u/100; u%=100;
	for(int i=0;i<digit && balance>=0;i++) res+=S2[balance--];
	digit=u/10; u%=10;
	for(int i=0;i<digit && balance>=0;i++) res+=S1[balance--];
	if(balance>=0 && balance<u) res++;
	return res;
}

void __cdecl main(){
	S0[0]=1;
	Expand(S0,S1,10);
	Expand(S1,S2,19);
	Expand(S2,S3,28);
	Expand(S3,S4,37);

	int m,n;
	for(;;){
		printf("m,n: ");
		scanf("%d%d",&m,&n);
		printf("Result: %d\n",NHappy(99999999)+1-NHappy(m)-NHappy(99999999-n));
	}
}

Проверок входных данных не делается. На простых тестах результаты были правильными. Работает, по моим оценкам, быстрее, чем за 2000 операций. Если нужно проверять много диапазонов, то можно предварительно вычислить частичные суммы Conv() в циклах, и тогда любой диапазон сосчитается за два десятка операций (и при этом не будет требовать гигабайта памяти).

UPD: Вот оптимизированный вариант:
int S[8][38];

void InitS(){
	for(int i=1;i<38;i++) S[0][i]=1;
	for(int a=1;a<9;a++){
		int s0=0;
		for(int i=1;i<=37;i++){
			s0+=S[a-1][i];
			if(i>10) s0-=S[a-1][i-10];
			S[a][i]=s0;
		}
	}
}

// number of tickets in 0..u-1
int NHappy(int u){
	int res=0;
	int balance=36;
	for(int a=7,b=10000000;a>=0;a--,b/=10){
		int digit=u/b;
		u%=b;
		int b1;
		if(a>3){
			balance-=9;
			res+=S[a][b1=balance+digit]-S[a][balance];
		} else{
			b1=balance-digit; if(b1<0) b1=-1;
			res+=S[a][balance+1]-S[a][b1+1];
		}
		balance=b1;
	}
	return res;
}

void __cdecl main(){
	InitS();
	int m,n;
	for(;;){
		printf("m,n: ");
		scanf("%d%d",&m,&n);
		printf("Result: %d\n",NHappy(99999999)+1-NHappy(m)-NHappy(99999999-n));
	}
}

Не спрашивайте, почему :)
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
butteff
@butteff
Раз в тысячу лет заправляю свитер в носки
Вот решение на php, в C++ я не очень силен, но думаю разбить строку на элементы и преобразовать их в массивы Вы сможете. Также думаю, что мой код понятен, синтаксис у php не сильно отличается от сишного. В общем, Вы справитесь.

<?php

$lucky = 0;
for ($i = 10000000; $i < 99999999; $i++) {
	$str = (string) $i;
    $sum1 = (int) $str[0] + (int) $str[1] + (int) $str[2] + (int) $str[3];
    $sum2 = (int) $str[4] + (int) $str[5] + (int) $str[6] + (int) $str[7];
    if ($sum1 == $sum2) {
    	$lucky++;
    }
}

echo $lucky;


UPD: код протестировал, этот, кажется, рабочий.
Скобки перед переменными - это преобразование типов.
Ответ написан
SagePtr
@SagePtr
Еда - это святое
Думаю, решать эту задачу примерно так:
Допустим,
M = AAAA BBBB
N = CCCC DDDD
1. Считаем массив сумм, который в исходном решении обозначен за C.
2. Считаем кол-во счастливых билетов от AAAA BBBB до AAAA 9999.
3. Считаем кол-во счастливых билетов от (AAAA+1) 0000 до (СССС-1) 9999 - то есть, массив сумм от AAAA+1 до CCCC-1 и поэлементно умножаем его на массив C из первого шага, получив в конечном итоге сумму.
4. Считаем кол-во счастливых билетов от CCCC 0000 до CCCC DDDD.
5. Суммируем все три количества на шагах 2-4, и задача решена.
Сложность каждой подзадачи такая же, как у исходной задачи. Думаю, понятно набросал алгоритм.

UPD: Ну и крайний случай учесть, если первые 4 цифры M и N совпадают - тогда всё это не надо считать, а провести только 1 перебор от правых 4 цифр первого числа до правых четырёх второго, и проверить их сумму цифр на равенство левой сумме цифр.
Ответ написан
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Задача на перестановки.
1. Формат AAAABBBB - билет счастливый, если:
сумма цифр частей AAAA=BBBB,
если равны левая и правая часть с любым порядком цифр.
Макс. кол-во счастливых билетов - 9000.
2. Если M < N, то сч.билетов<=9000.
3. Диапазон: сч.билетов=((N-M)/10000)%10000
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы