Combinatorics - Sweets

I found this problem somewhere in a competition and haven't been able to find a solution yet.

The boy has apples and keeps them in boxes. One box contains no more than N / 2. How many methods can he put candies in boxes.

So I am trying to implement a solution using DP. Here is my code:

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <unistd.h>
#include <vector>
#define size 1002
using namespace std;

long long a[size][size];
int n, k;
int main()
{
    cin >> n >> k;
    int kk = n/2;

    for(int i = 0; i <= k; ++i)
        a[0][i] = 1;
    a[0][0] = 0;

    for(int i = 0; i <= kk; ++i)
        a[i][1] = 1;

    for(int i = 1; i <= n; ++i) {

        for(int j = 2; j <= k; ++j) {
            int index = 0;
            long long res = 0;
            while(1) {
                res += a[i-index][j - 1];
                index += 1;

                if(index == kk + 1 || i-index < 0)
                    break;
            }
            a[i][j] = res;

        }
    }
    cout << a[n][k] << endl;

}

      

But the problem is that we have large numbers in the input, for example:

2 ≤ N ≤ 1000 - number of candies, N - even; 2 ≤ S ≤ 1000 - number of small boxes.

So, for an input like N = 1000 and S = 1000, I have to spend 5 * 10 ^ 8 operations. And the numbers are very big, so should I use BigInteger arithmetic?

Maybe there is an algorithm to implement the problem in linear time? Thanks and sorry for my english!

+3


source to share


1 answer


You can easily reduce the time complexity from O (kn ^ 2) to O (nk) with the following observation:

for(int i = 1; i <= n; ++i) {

    for(int j = 2; j <= k; ++j) {
        int index = 0;
        long long res = 0;
        while(1) {
            res += a[i-index][j - 1];
            index += 1;

            if(index == kk + 1 || i-index < 0)
                break;
        }
        a[i][j] = res;

    }
}

      

for everyone a[i][j]

, it is easy to see that

a[i][j] = sum a[k][j - 1]

from k from (i - n/2)

toi

So, if we create an array sum

to store the sum of all indices of the previous step, we can decrease one for the loop from the specified nested loop

a[i][j] = sum[i] - sum[i - (n/2) - 1];



Pseudocode:

long long sum[n + 1];
for(int j = 2; j <= k; ++j) {
    long long nxt[n + 1];
    for(int i = 1; i <= n; ++i) {
        int index = 0;
        long long res = sum[i] - sum[i - (n/2) - 1];

        a[i][j] = res;
        nxt[i] = nxt[i - 1] + a[i][j];//Prepare the sum array for next step

    }
    sum = nxt;
}

      

Note: This above code does not handle the initialization step for the array sum

, nor does it handle the case where i <n / 2. These cases should be obvious to handle.

Update:

My below Java solution is taken using a similar idea:

public static void main(String[] args) throws FileNotFoundException {
    // PrintWriter out = new PrintWriter(new FileOutputStream(new File(
    // "output.txt")));
    PrintWriter out = new PrintWriter(System.out);
    Scanner in = new Scanner();
    int n = in.nextInt();
    int s = in.nextInt();
    BigInteger[][] dp = new BigInteger[n + 1][2];
    BigInteger[][] count = new BigInteger[2][n + 1];
    int cur = 1;
    for (int i = 0; i <= n / 2; i++) {
        dp[i][0] = BigInteger.ONE;
        count[0][i] = (i > 0 ? count[0][i - 1]  : BigInteger.ZERO)
                .add(dp[i][0]);

    }
    for (int i = n / 2 + 1; i <= n; i++) {
        dp[i][0] = BigInteger.ZERO;
        count[0][i] = count[0][i - 1];
    }
    for (int i = 2; i <= s; i++) {
        for (int j = 0; j <= n; j++) {

            dp[j][cur] = dp[j][1 - cur].add((j > 0 ? count[1 - cur][j - 1]
                    : BigInteger.ZERO)
                    .subtract(j > n / 2 ? count[1 - cur][j - (n / 2) - 1]
                            : BigInteger.ZERO));

            count[cur][j] = (j > 0  ? count[cur][j - 1] : BigInteger.ZERO)
                    .add(dp[j][cur]);
        }
        cur = 1 - cur;
    }
    out.println(dp[n][1 - cur]);
    out.close();
}

      

+1


source







All Articles