web-dev-qa-db-fra.com

Nombre de façons de diviser n Objets en K Groupes K, de sorte qu'aucun groupe n'aura moins d'objets que des groupes précédemment formés?

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.

4
Double A

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;
}
0
Nj Rafi

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));
}
0