I would say it's not a homework problem. It's just a tutorial resource online to learn the dynamic programming concepts from USACO website.
In the resource, a problem was given as follows.
Question:
A sequcen of as many as 10000 integers, ( 0 < integer < 100,000), what is the maximum decreasing subsequence?
The decent recursive approach was given
1 #include <stdio.h>
2 long n, sequence[10000];
3 main () {
4 FILE *in, *out;
5 int i;
6 in = fopen ("input.txt", "r");
7 out = fopen ("output.txt", "w");
8 fscanf(in, "%ld", &n);
9 for (i = 0; i < n; i++) fscanf(in, "%ld", &sequence[i]);
10 fprintf (out, "%d\n", check (0, 0, 99999));
11 exit (0);
12 }
13 check (start, nmatches, smallest) {
14 int better, i, best=nmatches;
15 for (i = start; i < n; i++) {
16 if (sequence[i] < smallest) {
17 better = check (i, nmatches+1, sequence[i]);
18 if (better > best) best = better;
19 }
20 }
21 return best;
22 }
Guys, I am not good at the algorithmic analysis. Would you please tell me what's the Big-O notation to this recursive enumeration solution in worst case as tight as possible. My personal thought would be O(N^N), but I have no confidence. Because the runtime is still acceptable under N <= 100. There must be something wrong. Please help me. Thank you.
In the USACO website, it gives the dynamic programming approach in O(n^2) as follows.
1 #include <stdio.h>
2 #define MAXN 10000
3 main () {
4 long num[MAXN], bestsofar[MAXN];
5 FILE *in, *out;
6 long n, i, j, longest = 0;
7 in = fopen ("input.txt", "r");
8 out = fopen ("output.txt", "w");
9 fscanf(in, "%ld", &n);
10 for (i = 0; i < n; i++) fscanf(in, "%ld", &num[i]);
11 bestsofar[n-1] = 1;
12 for (i = n-1-1; i >= 0; i--) {
13 bestsofar[i] = 1;
14 for (j = i+1; j < n; j++) {
15 if (num[j] < num[i] && bestsofar[j] >= bestsofar[i]) {
16 bestsofar[i] = bestsofar[j] + 1;
17 if (bestsofar[i] > longest) longest = bestsofar[i];
18 }
19 }
20 }
21 fprintf(out, "bestsofar is %d\n", longest);
22 exit(0);
23 }
Just look at with what kind of parameters you call the function. The first determines the third (which btw means you needed have the third parameter). The first ranges between 0 and n. The second one is smaller than the first. This means that you have at most n^2 different calls to the function.
Now comes the question how many times you call the function with the same parameters. And the answer is simple: you actually generate every single decreasing subsequece. This means that for the sequence N, N-1, N-2, ... you will generate 2^N sequences. Pretty poor, right (if you want experiment with the sequence I have given you)?
However if you use the memoization technique you should have already read about, you can improve the complexity to N^3 (at most n operations in every call to the function, the different calls are N^2 and memoization allows you to pay only once for a different call).
Related
I'm currently learning algorithms and have came across a code challenge from an interviewer about a function that prints out the nth prime number sequentially. So it would be something like:
getPrimeNth(10) will print 1 2 3 5 7 11 13 17 19 23
but most of the ones I found will print out just the nth number, so 23, or just ones that will detect if it is prime numbers. I am going to risk getting downvoted for this but I can't seem to find the right solution for this.
One is not a prime, for starters.
Second, your question needs more clarification....
Primes are not challenging - there is a lot of information available.
The simplest solution for you would be to simply test every number by modding up to the square root of that number. If it mods to zero, it is not prime. Store the primes in an array one after another. I'm not going to straight up give you the answer, but read more about The Sieve of Eratosthenes - which is highly inefficient IMO, but where you must start.
Therefore, the first prime would be in slot 0, second in slot 1, etc, etc.
The below code tries to find and saves all possible primes upto N (defined by the macro). It just calls the utility function is_prime() which checks whether a given number is prime or not.
#define TRUE 1
#define FALSE 0
#define N 10
typedef short int bool;
bool is_prime(int num)
{
int i = 2;
for (i = 2; i <= (num - 1); i++) {
if ((num % i) == 0) {
return FALSE;
}
}
return TRUE;
}
int main()
{
int primes[N];
int num_primes = 0;
int num = 2; /* start with 2 */
while (num_primes != N) {
if (is_prime (num) == TRUE) {
primes[num_primes] = num;
num_primes++;
}
num++;
}
int i = 0;
for (i = 0; i < N; i++) {
printf ("%d ", primes[i]);
}
printf ("\n");
}
Output: 2 3 5 7 11 13 17 19 23 29
I should resolve 16-Queens Problem in 1 second.
I used backtracking algorithm like below.
This code is enough to resolve N-Queens Problem in 1 second when the N is smaller than 13.
But it takes long time if N is bigger than 13.
How can I improve it?
#include <stdio.h>
#include <stdlib.h>
int n;
int arr[100]={0,};
int solution_count = 0;
int check(int i)
{
int k=1, ret=1;
while (k < i && ret == 1) {
if (arr[i] == arr[k] ||
abs(arr[i]-arr[k]) == abs(i-k))
ret = 0;
k++;
}
return ret;
}
void backtrack(int i)
{
if(check(i)) {
if(i == n) {
solution_count++;
} else {
for(int j=1; j<=n; j++) {
arr[i+1] = j;
backtrack(i+1);
}
}
}
}
int main()
{
scanf("%d", &n);
backtrack(0);
printf("%d", solution_count);
}
Your algorithm is almost fine. A small change will probably give you enough time improvement to produce a solution much faster. In addition, there is a data structure change that should let you reduce the time even further.
First, tweak the algorithm a little: rather than waiting for the check all the way till you place all N queens, check early: every time you are about to place a new queen, check if another queen is occupying the same column or the same diagonal before making the arr[i+1] = j; assignment. This will save you a lot of CPU cycles.
Now you need to speed up checking of the next queen. In order to do that you have to change your data structure so that you could do all your checks without any loops. Here is how to do it:
You have N rows
You have N columns
You have 2N-1 ascending diagonals
You have 2N-1 descending diagonals
Since no two queens can take the same spot in any of the four "dimensions" above, you need an array of boolean values for the last three things; the rows are guaranteed to be different, because the i parameter of backtrack, which represents the row, is guaranteed to be different.
With N up to 16, 2N-1 goes up to 31, so you can use uint32_t for your bit arrays. Now you can check if a column c is taken by applying bitwise and & to the columns bit mask and 1 << c. Same goes for the diagonal bit masks.
Note: Doing a 16 Queen problem in under a second would be rather tricky. A very highly optimized program does it in 23 seconds on an 800 MHz PC. A 3.2 GHz should give you a speed-up of about 4 times, but it would be about 8 seconds to get a solution.
I would change while (k < i && ret == 1) { to while (k < i) {
and instead of ret = 0; do return 0;.
(this will save a check every iteration. It might be that your compiler does this anyway, or some other performance trick, but this might help a bit).
I'm trying to work on a sub-problem of an larger algorithm which I am really struggling on!
The Problem
If I had a array of numbers (say A), how can I efficiently list all the numbers that can be made by multiplying the numbers together (which can be used as many times as you want) and is less than another number (say x).
For example, let's say I had A = [7, 11, 13] and x was 1010, the answers would be:
- 7 = 7
- 11 = 11
- 13 = 13
- 7*7 = 49
- 7*11 = 77
- 7*13 = 91
- 11*11 = 121
- 11*13 = 143
- 13*13 = 169
- 7*7*7 = 343
- 7*7*11 = 539
- 7*7*13 = 637
- 7*11*11 = 847
- 7*11*13 = 1001
I tried my best not to miss any (but feel free to edit if I have)!
I can tell this is probably some type of recursion but am really struggling on this one!
Optional
A naive solution will also be nice (that's how much I'm struggling).
Running time is also optional.
UPDATE
All numbers in A are all the prime numbers (except 1, 2, 3, 5) got from the sieve of eratosthenes.
UPDATE 2
A is also sorted
UPDATE 3
All numbers in A is under the limit
UPDATE 4
The solution does NOT need to be recursion. That was just an idea I had. And Java or Pseudo code more preferable!
I'd go with using a queue. The algorithm I have in mind would be something like the following (in pseudocode):
multiplyUntil(A, X)
{
queue q = A.toQueue();
result;
while(!q.isEmpty())
{
element = q.pop();
result.add(element); // only if the initial elements are guaranteed to be < X otherwise you should add other checks
for(int i = 0; i < A.length; i++)
{
product = element * A[i];
// A is sorted so if this product is >= X the following will also be >= X
if(product >= X)
{
// get out of the inner cycle
break;
}
q.add(product);
}
}
return result;
}
Let me know if something is unclear.
P.S: Keep in mind that the result is not guaranteed to be sorted. If you want the result to be sorted you could use a heap instead of a queue or sort the result in the end of the computation.
Here's solution on Java along with comments. It's pretty straightforward to translate it to other language.
// numbers is original numbers like {7, 11, 13}, not modified
// offset is the offset of the currently processed number (0 = first)
// limit is the maximal allowed product
// current array is the current combination, each element denotes
// the number of times given number is used. E. g. {1, 2, 0} = 7*11*11
private static void getProducts(int[] numbers, int offset, int limit, int[] current) {
if(offset == numbers.length) {
// all numbers proceed: output the current combination
int product = 1;
StringBuilder res = new StringBuilder();
for(int i=0; i<offset; i++) {
for(int j = 0; j<current[i]; j++) {
if(res.length() > 0) res.append(" * ");
res.append(numbers[i]);
product *= numbers[i];
}
}
// instead of printing you may copy the result to some collection
if(product != 1)
System.out.println(" - "+res+" = "+product);
return;
}
int n = numbers[offset];
int count = 0;
while(limit >= 1) {
current[offset] = count;
getProducts(numbers, offset+1, limit, current);
count++;
// here the main trick: we reduce limit for the subsequent recursive calls
// note that in Java it's integer division
limit/=n;
}
}
// Main method to launch
public static void getProducts(int[] numbers, int limit) {
getProducts(numbers, 0, limit, new int[numbers.length]);
}
Usage:
public static void main(String[] args) {
getProducts(new int[] {7, 11, 13}, 1010);
}
Output:
- 13 = 13
- 13 * 13 = 169
- 11 = 11
- 11 * 13 = 143
- 11 * 11 = 121
- 7 = 7
- 7 * 13 = 91
- 7 * 11 = 77
- 7 * 11 * 13 = 1001
- 7 * 11 * 11 = 847
- 7 * 7 = 49
- 7 * 7 * 13 = 637
- 7 * 7 * 11 = 539
- 7 * 7 * 7 = 343
The resulting products are sorted in different way, but I guess sorting is not a big problem.
Here is my solution in C++. I use a recursive function. The principle is:
the recursive function is given a limit, a current which is a composite and a range of primes [start, end(
it will output all combination of powers of the primes in the given range, multiplied by the current composite
At each step, the function takes the first prime p from the range, and compute all its powers. It then multiplies current by the p as long as the product, cp is under the limit.
We use the fact the array is sorted by leaving as soon as cp is above the limit.
Due to the way we compute the numbers they won't be sorted. But it is easy to add this as a final step once you collected the numbers (in which case ou would use a back_inserter output iterator instead of an ostream_iterator, and do a sort on the collection vector)
#include <algorithm>
#include <iostream>
#include <iterator>
using namespace std;
template <class It, class Out>
void f(int limit, int current, It start, It end, Out out) {
// terminal condition
if(start == end) {
if(current != 1)
*(out++) = current;
return;
}
// Output all numbers where current prime is a factor
// starts at p^0 until p^n where p^n > limit
int p = *start;
for(int cp = current; cp < limit; cp *= p) {
f(limit, cp, start+1, end, out);
}
}
int main(int argc, char* argv[]) {
int const N = 1010;
vector<int> primes{7, 11, 13};
f(N, 1, begin(primes), end(primes), ostream_iterator<int>(cout, "\n"));
}
I found this iterative algorithm that prints the power set for a given set:
void PrintSubsets()
{
int source[3] = {1,2,3};
int currentSubset = 7;
int tmp;
while(currentSubset)
{
printf("(");
tmp = currentSubset;
for(int i = 0; i<3; i++)
{
if (tmp & 1)
printf("%d ", source[i]);
tmp >>= 1;
}
printf(")\n");
currentSubset--;
}
}
However, I am not sure why it works. Is it similar to a solution where you use a set of n bits, and on each step, add 1 with carry, using the reuslting pattern of zeros and ones to determine which elements belong?
List all integers in the binary base, and light should shine:
{abc}
7 xxx
6 xx-
5 x-x
4 x--
3 -xx
2 -x-
1 --x
0 --- (omitted)
The order to enumerate the integers does not matter provided you list them all. Incrementing or decrementing are the most natural ways.
The question is as follows
Consider the number triangle shown below. Write a program that calculates the highest sum of numbers that can be passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
In the sample above, the route from 7 to 3 to 8 to 7 to 5 produces the highest sum: 30.
I had the following error
Your program had this runtime error: Bad
syscall #32000175 (RT_SIGPROCMASK) [email kolstad if you think
this is wrong]. The program ran for 0.259 CPU seconds before the
error. It used 16328 KB of memory.
The code is as follows.
int arr[1500][1500];
map < int,map < int,int> >dp;
int main()
{
// ofstream fout ("numtri.out");
// ifstream fin ("numtri.in");
int n;
// fin>>n;
freopen ("numtri.in", "r", stdin);
freopen ("numtri.out", "w", stdout);
scanf ("%d", &n);
int ct = 1;
int gaga = -100;
for (int i=0; i<n; i++)
{
for (int j=0; j<ct; j++)
{
scanf ("%d", &arr[i][j]);
if(i>0)
dp[i][j] = maxi (dp[i-1][j-1] + arr[i][j], dp[i-1][j] + arr[i][j]);
else
dp[0][0]=arr[0][0];
if (i == n-1)
{
if (dp[i][j] > gaga)
gaga=dp[i][j];
}
}
ct++;
}
printf ("%d\n", gaga);
return 0;
}
It works fine on my laptop. On the website it works for 8 test cases and fails for 9th one with this error.
Thanks for the help!
if(i>0)
dp[i][j]=maxi(dp[i-1][j-1]+arr[i][j],dp[i-1][j]+arr[i][j]);
You check if i > 0, which will ensure you never access a negative index. You never do the same for j however, so you will access dp[i-1][-1] on the first run of the inner (j) loop. I'm pretty sure this is what causes the error.