Exemple: n = 8, k = 4 réponse: 5
[1,1,1,1,5], [1,1,2,4], [1,1,3,3], [1,2,2,3], [2,2,2,2]
J'ai pensé à appliquer une programmation dynamique pour compter le nombre de façons 8 Les objets peuvent être divisés en 4 groupes, mais ne comprennent pas comment garder une trace du nombre d'objets dans le groupe précédent.
Approche DP:
for(int j=0;j<=n;j++)
{
for(int i=1;i<=k;i++)
{
if(j<i)
continue;
if(j==i)
dp[j]=1;
for(int k=1;k<i;k++)
{
dp[j]+=dp[k]*dp[j-k];
}
}
}
S'il vous plaît aider avec l'approche. Je suis relativement nouveau à DP.
De l'exemple, je suppose que aucun groupe ne peut être vide. En supposant également la valeur de n, k <= 1000.
Les États DP seront remaining Objects
Et remaining Groups
. f(remObject,remGroup)
_ sera le nombre de façons de mettre remObject
in remGroup
si aucun groupe aura moins d'objets que des groupes formés précédemment.
Nous considérerons 2 cas.
Si nous voulons mettre un objet dans la plupart des groupes de gauche, nous devrons également mettre un objet à tous les autres groupes. Nous devons donc nous assurer remaining Objects >= remaining Groups
. Dans ce cas, nous ajouterons f(remObject - remGroup, remGroup)
à notre réponse.
Si nous ne voulons plus mettre un objet au plus grand groupe, nous ajouterons f(remObject,remGroup - 1)
avec notre réponse.
Et le cas de base sera lorsque aucun groupe n'est laissé à considérer et que tous les objets sont placés.
Comme tous les groupes ne peuvent pas être vides avant d'appeler notre DP, nous mettrons 1 objet dans tous les groupes K.
Regardez le code pour plus de détails.
#define mxn 1003
#define i64 long long int
#define mod 1000000007
i64 dp[mxn][mxn];
i64 f(int remObject,int remGroup) {
if(!remGroup) {
if(!remObject)
return 1;
return 0;
}
if(dp[remObject][remGroup] != -1)
return dp[remObject][remGroup];
i64 ans = 0;
if(remObject >= remGroup)
ans += f(remObject - remGroup, remGroup);
ans += f(remObject,remGroup - 1);
ans %= mod;
return dp[remObject][remGroup] = ans;
}
int main()
{
int t,n,k;
memset(dp,-1,sizeof dp);
cin >> t;
while(t--) {
cin >> n >> k;
if(n < k)
cout << 0 << endl;
else
cout << f(n-k,k) << endl;
}
return 0;
}
La solution mémotrice peut être encore améliorée en ajoutant peu de chèques comme. Si N, K sont égaux à la réponse est 1. Nous n'avons pas besoin de faire de la récursion pour 1000, 17 000. De plus de K est 1, peu importe ce que n est la réponse est 1. 1000,1 est 1 SO SE SE SE SOIT EN SECTEUR ET TEMPS. Code mis à jour: impossible d'ajouter ceci comme commentaire à la solution ci-dessus en raison d'une mauvaise réputation, désolé. Vous pouvez également trouver une explication simple ici: N dans la récursion de groupes K .
function f(n, k, memo = {}) {
if (k == 0 && n == 0) return 1;
if (k == 1 && n != 0) return 1; //when k is 1 no matter what n is
if (n == k) return 1; // when k and n are equal.
if (n <= 0 || k <= 0) return 0;
let key = String([n, k]); // Thanks to comment by user633183
if (memo.hasOwnProperty(key)) return memo[key];
return (memo[key] = f(n - k, k, memo) + f(n - 1, k - 1, memo));
}