user584397
user584397

Reputation: 347

minimal positive number divisible to N

1<=N<=1000

How to find the minimal positive number, that is divisible by N, and its digit sum should be equal to N.

For example:
N:Result
1:1
10:190

And the algorithm shouldn't take more than 2 seconds.

Any ideas(Pseudocode,pascal,c++ or java) ?

Upvotes: 7

Views: 6589

Answers (5)

Priyank Bhatnagar
Priyank Bhatnagar

Reputation: 814

After thinking about it a bit, I think I have found the expected answer.

Think of it as a graph. For any number, you can make new number by multiplying that number by 10 and adding any of the digits 0-9. You will need to use BFS to reach the smallest number first.

For every node maintain sum and remainder. Using these values you can move to the adjacent nodes, also these values will help you avoid reaching useless states again and again. To print the number, you can use these values to trace your steps. Complexity is O(n^2), in worst case table is completely filled. (See code)

Note : Code takes number of test cases first. Works under 0.3s for n<=1000.

[Edit] : Ac on spoj in 6.54s. Test files have 50 numbers.

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define F first
#define S second 
#define N 1100
#define mp make_pair
queue<pair<int, int> >Q;
short sumTrace[N][N], mulTrace[N][N];

void print(int sum, int mul) {
    if (sumTrace[sum][mul] == 42)return;
    print(sum-sumTrace[sum][mul], mulTrace[sum][mul]);
    printf("%d",sumTrace[sum][mul]);
}
void solve(int n) {
    Q.push(mp(0,0));
    sumTrace[0][0]=42; // any number greater than 9
    while (1) {
        int sum = Q.front().F;
        int mul = Q.front().S;
        if (sum == n && mul == 0) break;
        Q.pop();
        for (int i=0; i<10; i++) {
            int nsum = sum+i;
            if (nsum > n)break;
            int nmul = (mul*10+i)%n;
            if (sumTrace[nsum][nmul] == -1) {
                Q.push(mp(nsum, nmul));
                sumTrace[nsum][nmul] = i;
                mulTrace[nsum][nmul] = mul;
            }
        }
    }
    print(n,0);
    while(!Q.empty())Q.pop();
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        memset(sumTrace, -1, sizeof sumTrace);
        solve(n);
        printf("\n");
    }
    return 0;
}

Upvotes: 0

IVlad
IVlad

Reputation: 43477

The maximum digit sum you have to worry about is 1000. Since 1000 / 9 = ~100 This is actually not a lot, so I think the following should work:

Consider the following data structure:

entry { int r, sum, prev, lastDigit; }

Hold a queue of entry where initially you have r = 1 mod N, sum = 1, prev = -1, lastDigit = 1; r = 2 mod N, sum = 2, prev = -1, lastDigit = 2 etc.

When you extract an entry x from the queue:

y = new entry
for i = 0 to 9 do
  y.r = (x.r * 10 + i) % N
  y.sum = x.sum + i
  y.prev = <position of x in the queue>
  y.lastDigit = i

  if y.r == 0 and y.sum == N
    // you found your multiple: use the prev and lastDigit entries to rebuild it

  if y.sum < N then
    queue.add(y)

This is basically a BFS on the digits. Since the maximum sum you care about is small, this should be pretty efficient.

Upvotes: 0

paxdiablo
paxdiablo

Reputation: 881293

Sure, pseudo-code, since it smells like homework :-)

def findNum (n):
    testnum = n
    while testnum <= 1000:
        tempnum = testnum
        sum = 0
        while tempnum > 0:
            sum = sum + (tempnum mod 10)
            tempnum = int (tempnum / 10)
        if sum == n:
            return testnum
        testnum = testnum + n
    return -1

It takes about 15 thousandths of a second when translated to Python so well under your two-second threshold. It works by basically testing every multiple of N less than or equal to 1000.

The test runs through each digit in the number adding it to a sum then, if that sum matches N, it returns the number. If no number qualifies, it returns -1.

As test cases, I used:

 n  findNum(n)     Justification
==  ==========     =============
 1           1       1 =  1 *  1,  1 = 1
10         190     190 = 19 * 10, 10 = 1 + 9 + 0
13         247     247 = 13 * 19, 13 = 2 + 4 + 7
17         476     476 = 17 * 28, 17 = 4 + 7 + 6
99          -1     none needed

Now that only checks multiples up to 1000 as opposed to checking all numbers but checking all numbers starts to take much more than two seconds, no matter what language you use. You may be able to find a faster algorithm but I'd like to suggest something else.

You will probably not find a faster algorithm than what it would take to simply look up the values in a table. So, I would simply run a program once to generate output along the lines of:

int numberDesired[] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    190, 209, 48, 247, 266, 195, 448, 476, 198, 874,
    ...
    -1, -1};

and then just plug that into a new program so that it can use a blindingly fast lookup.

For example, you could do that with some Python like:

print "int numberDesired[] = {"
for i in range (0, 10):
    s = "    /* %4d-%4d */"%(i*10,i*10+9)
    for j in range (0, 10):
        s = "%s %d,"%(s,findNum(i*10+j))
    print s
print "};"

which generates:

int numberDesired[] = {
    /*    0-   9 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    /*   10-  19 */ 190, 209, 48, 247, 266, 195, 448, 476, 198, 874,
    /*   20-  29 */ 3980, 399, 2398, 1679, 888, 4975, 1898, 999, 7588, 4988,
    /*   30-  39 */ 39990, 8959, 17888, 42999, 28798, 57995, 29988, 37999, 59888, 49998,
    /*   40-  49 */ 699880, 177899, 88998, 99889, 479996, 499995, 589996, 686999, 699888, 788998,
    /*   50-  59 */ 9999950, 889899, 1989988, 2989889, 1999998, 60989995, 7979888, 5899899, 8988898, 8888999,
    /*   60-  69 */ 79999980, 9998998, 19999898, 19899999, 59989888, 69999995, 67999998, 58999999, 99899888, 79899999,
:
};

That will take a lot longer than two seconds, but here's the thing: you only have to run it once, then cut and paste the table into your code. Once you have the table, it will most likely blow away any algorithmic solution.

Upvotes: 0

kilotaras
kilotaras

Reputation: 1419

Let f(len, sum, mod) be a bool, meaning we can build a number(maybe with leading zeros), that has length len+1, sum of digits equal to sum and gives mod when diving by n.

Then f(len, sum, mod) = or (f(len-1, sum-i, mod- i*10^len), for i from 0 to 9). Then you can find minimal l, that f(l, n, n) is true. After that just find first digit, then second and so on.

#define FOR(i, a, b) for(int i = a; i < b; ++i)
#define REP(i, N) FOR(i, 0, N)

#define FILL(a,v) memset(a,v,sizeof(a))



const int maxlen = 120;
const int maxn = 1000;

int st[maxlen];
int n;

bool can[maxlen][maxn+1][maxn+1];
bool was[maxlen][maxn+1][maxn+1];

bool f(int l, int s, int m)
{
    m = m%n;
    if(m<0)
        m += n;

    if(s == 0)
        return (m == 0);
    if(s<0)
        return false;
    if(l<0)
        return false;   

    if(was[l][s][m])
        return can[l][s][m];

    was[l][s][m] = true;
    can[l][s][m] = false;

    REP(i,10)
        if(f(l-1, s-i, m - st[l]*i))
        {
            can[l][s][m] = true;
            return true;
        }
    return false;
}


string build(int l, int s, int m)
{
    if(l<0)
        return "";
    m = m%n;
    if(m<0)
        m += n;
    REP(i,10)
        if(f(l-1, s-i, m - st[l]*i))
        {
            return char('0'+i) + build(l-1, s-i, m - st[l]*i);
        }
    return "";
}

int main(int argc, char** argv)
{
    ios_base::sync_with_stdio(false);

    cin>>n;
    FILL(was, false);   
    st[0] = 1;
    FOR(i, 1, maxlen)
        st[i] = (st[i-1]*10)%n;
    int l = -1;
    REP(i, maxlen)
        if(f(i, n, n))
        {
            cout<<build(i,n,n)<<endl;
            break;
        }

    return 0;
}

NOTE that this uses ~250 mb of memory.

EDIT: I found a test where this solution runs, a bit too long. 999, takes almost 5s.

Upvotes: 1

Joni
Joni

Reputation: 111239

Update: I understood that the result was supposed to be between 0 and 1000, but no. With larger inputs the naïve algorithm can take a considerable amount of time. The output for 80 would be 29999998880.


You don't need a fancy algorithm. A loop that checks your condition for 1000 numbers will take less than 2 seconds on any reasonably modern computer, even in interpreted languages.

If you want to make it smart, you only need to check numbers that are multiples of N. To further restrict the search space, the remainders of N and the result have to be equal when divided by 9. This means that now you have to check only one number in every 9N.

Upvotes: 0

Related Questions