Meu problema. Dada , quero contar o número de válido multisets . Um multiset é válido se
- A soma dos elementos de é , e
- Cada número de a pode ser expressa de forma única como uma soma de alguns dos elementos de .
Exemplo. Por exemplo, se , são válidos.
No entanto, é inválido porque 2 pode ser formado por e (ou seja, 2 pode ser expresso como e ) , portanto, a segunda condição não é válida. Da mesma forma 3 pode ser formado por e .
} também é inválido porque todos os números de a podem ser feitos exclusivamente, mas a soma dos elementos de não é .
Eu tentei encontrar um bom algoritmo para esse problema por algum tempo, mas não consigo resolvê-lo. É de codechef . Eu já vi algumas das soluções enviadas, mas ainda não consegui obter a lógica para resolver o problema. NOTA: O limite de tempo para a pergunta é de 10 segundos e
Para um multiset, usarei a notação se , o que significa ocorre vezes na multiconjunto S.
Até agora eu tirei algumas conclusões
- O primeiro elemento do multiset classificado necessário deve ser
- Seja é um conjunto seguindo as duas propriedades, então ∀ r < k a r + 1 = a r ou ( ∑ r i = 0 a i ) + 1
- Seja , onde a i ocorre c i vezes, segue as propriedades necessárias e, a partir da conclusão acima, podemos dizer que ∀ i a i | n + 1 e se j > i .
Prova: a i + 1 = ( a i c i + a i - 1 ) + 1 ⇒ a i | a i + 1
- Agora, considere ou seja, todo o os números subseqüentes após 1 serão múltiplos de d . Então vamos f ( n )seja possível a contagem desse multiset, então onde estou somando todo o número possível de1's(=d-1). Em outros termosf(n-1)=g(n)=∑d| n,d≠ng(d)
Finalmente, meu problema é reduzido a isso - encontre maneira eficiente para que não exceda o limite de tempo.
fonte
Respostas:
Aqui está o que a solução mais rápida está fazendo. Na verdade, está computando sua função Dado n , nós o fatoramos (veja abaixo) e depois calculamos todos os fatores (veja abaixo) f 1 , … , f m em alguma ordem tal que f i | f j implica i ≤ j (propriedade P). Agora calculamos g de acordo com a fórmula, revisando os fatores na ordem dada. A propriedade P garante que, quando calculamos g ( d ) , já calculamos g ( e ) para todos os fatores não triviais e
Mais detalhadamente, examinamos os fatores em ordem e, para cada fator , encontramos todos os seus fatores não triviais, verificando qual de f 1 , ... , f i - 1 divide f i .fi f1,…,fi−1 fi
Factoring: Pré - processamento: fazemos uma lista de todos os números primos abaixo de usando a peneira de Eratóstenes. Dado n , simplesmente usamos a divisão de teste.109 n
Gerando todos os fatores: isso é feito recursivamente. Suponha que . Executamos t loops aninhados l 1 ∈ { 0 , … , k 1 } , … , l t ∈ { 0 , … , k t } e produzimos p l 1 1 ⋯ p l t t . Você pode provar a propriedade P por indução.n=pk11⋯pktt t l1∈{0,…,k1},…,lt∈{0,…,kt} pl11⋯pltt
Otimização: Como o programa é executado em várias entradas, podemos usar a memorização para economizar tempo em diferentes entradas. Memorizamos apenas valores pequenos (até ), e isso nos permite armazenar todos os valores memorizados em uma matriz. A matriz é inicializada com zeros e, portanto, podemos dizer quais valores já são conhecidos (já que todos os valores calculados são positivos).105
Se a fatoração primária de for p k 1 1 , … , p k t t , então f ( n ) depende apenas de ( k 1 , … , k t ) e, de fato, apenas da versão classificada desse vetor . Todo número abaixo de 10 9 tem no máximo 29 fatores primos (com repetição) e, como p ( 29 ) = 4565 , parece possível calcular fn+1 pk11,…,pktt f(n) (k1,…,kt) 109 29 p(29)=4565 f (ou melhor, ) para todos eles, recursivamente. Essa solução poderia ser mais rápida se houvesse muitas entradas diferentes; como é, existem no máximo 10 .g 10
Também é possível que essa função, mapeando partições para o correspondente , tenha uma forma analítica explícita. Por exemplo, g ( p k ) = 2 k - 1 , g ( p 1 … p t ) é dado por A000670 e g ( p 2 1 p 2 … p t ) é dado por A005649 ou A172109 .g g(pk)=2k−1 g(p1…pt) g(p21p2…pt)
fonte
OK, então você tem uma relação de recorrência para (consulte o final de sua pergunta).g(⋅)
Nesse ponto, parece que uma abordagem natural seria escrever o algoritmo recursivo para calcular e aplicar a memorização para não computar g ( i ) mais de uma vez. Em outras palavras, quando você calcula g ( i ) , armazena-o em uma tabela de hash que mapeia i ↦ g ( i ) ; se precisar conhecer g ( i ) novamente no futuro, procure-o na tabela de hash.g(n) g(i) g(i) i↦g(i) g(i)
fonte
Aqui está uma dica para você começar. Aplique algoritmos de programação dinâmica padrão para enumerar o conjunto de partições de um número inteiro e adicione alguma lógica para verificar qual delas permite a realização de alterações exclusivas, verificando iterativamente todas as somas, fazendo alterações e verificando a exclusividade.
Aqui está uma abordagem que provavelmente será melhor.
Isso deve ser bem factível. Boa sorte! Diverta-se! Trabalhar com os detalhes será um bom exercício de aprendizado em programação dinâmica.
fonte