I came across an article that has the following statement:
maxSubArray(A, i) = maxSubArray(A, i - 1) > 0 ? maxSubArray(A, i - 1) : 0 + A[i];
My question is, would maxSubArray(A, i - 1) be evaluated (called) twice (if its value is greater than 0)? Does it increase the time complexity of the code? I think so, since we would end up calling the recursive function twice (if its value is greater than 0).
Edit: Here's the code:
public int maxSubArray(int[] A) {
int n = A.length;
int[] dp = new int[n];//dp[i] means the maximum subarray ending with A[i];
dp[0] = A[0];
int max = dp[0];
for(int i = 1; i < n; i++){
dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);
max = Math.max(max, dp[i]);
}
return max;
}
and here is the related link. The above code is not directly related, since the one in my original question is from top-down DP approach, while the one added later on is from a bottom-up DP approach.
This:
maxSubArray(A, i) = maxSubArray(A, i - 1) > 0 ? maxSubArray(A, i - 1) : 0 + A[i];
is just a pseudo code notation of the relation between maxSubArray(A, i - 1) and maxSubArray(A, i). It just tells us how to compute the result for i when we do know the result for i-1. Read it like maths notation. Similar as
y = 5 * x
describes
int foo(int x) { return x*5; }
In the actual implementation the above recurrence relation is realized via:
dp[i] = A[i] + (dp[i - 1] > 0 ? dp[i - 1] : 0);
Here dp[i - 1] is merely accessing an element of an array. Accessing the same array element twice has no impact on complexity. Given that dp[i-1] is not modified in that line, a compiler might optimize it to access dp[i-1] only once.
In the presence of recursion, unnecessarily calling a function twice can have an impact on complexity. Consider this example (stolen from Jarod42):
int f(int n) {
if (n == 0) return 1;
return 2 * f(n - 1);
}
int f(int n) {
if (n == 0) return 1;
return f(n - 1) + f(n - 1);
}
Both yield the same result, but the first has linear complexity while the second is exponential.
Related
I have the following code. I have been told that the time complexity of this is O(n).
But I am having a hard time understanding how. To me it seems like it is
O(n) + O(n) + O(n) = O(3n)
and the space complexity is O(3n) as well because 3 vectors that get populated to the size of n.
Is my understanding correct ?
void productofself(std::vector<int> v)
{
int multipliedProduct = 0;
std::vector<int> left;
std::vector<int> right(v.size());
std::vector<int> result;
for (int i = 0; i < v.size(); i++)
{
if (i == 0)
{
multipliedProduct = 1;
}
else
{
multipliedProduct *= v[i - 1];
}
left.push_back(multipliedProduct);
}
for (int i = v.size() - 1; i >= 0; i--)
{
if (i == v.size() - 1)
{
multipliedProduct = 1;
}
else
{
multipliedProduct *= v[i + 1];
}
right[i]=multipliedProduct;
}
for (int i = 0; i < v.size(); i++)
{
result.push_back(left[i] * right[i]);
}
}
I think you are missing the point of the asymptotic notation. That code is O(3n) but it is also O(n) and the latter is what matters.
O(3n) and O(n) are equivalent by definition.
We say f(x) is O( g(x) ) if there exist two constants m and k such that f(x) < m * g(x) for all x greater than k. Basically f(x) is O(g(x)) if for sufficiently large x, f(x) is always smaller than a constant factor times g(x).
So you can see the constant factor right in that definition: m. A scaling factor like 3 comes out in the wash. There exists an m such that 3 * n < m * n, namely any number greater than 3. So the function f(n) = 3*n is O(n).
The Question:
Given an array A of N distinct integers and an array B of integers(need not be distinct). Find the min no. of numbers that need to be added to B to make A a subsequence of it.
My Strategy:
Quite simple- find the longest common subsequence, lcs and so the answer is sizeof(A) - lcs.
My Code:
int lcs(vector<int>A, vector<int>B, int n, int m)
{
int L[m + 1][n + 1];
int i, j;
/* Following steps build L[m+1][n+1] in
bottom up fashion. Note that L[i][j]
contains length of LCS of X[0..i-1]
and Y[0..j-1] */
for (i = 0; i <= m; i++)
{
for (j = 0; j <= n; j++)
{
if (i == 0 || j == 0)
L[i][j] = 0;
else if (B[i - 1] == A[j - 1])
L[i][j] = L[i - 1][j - 1] + 1;
else
L[i][j] = max(L[i - 1][j], L[i][j - 1]);
}
}
/* L[m][n] contains length of LCS
for A[0..n-1] and B[0..m-1] */
return (n - L[m][n]);
}
My output:
I am getting wrong output. (Differing mostly by 1.) I was also getting TLE for some test cases.
can someone locate where i am going wrong in logic or in code?
If A == [1, 2, 3, 4, 5] and B == [1, 2, 4, 5] then longest common sequence is 2 and your answer is 3, but you only need to add a single number 3 to B to meet the requirements. So the overall logic seems incorrect
Problem statement: Given a set of n coins of some denominations (maybe repeating, in random order), and a number k. A game is being played by a single player in the following manner: Player can choose to pick 0 to k coins contiguously but will have to leave one next coin from picking. In this manner give the highest sum of coins he/she can collect.
Input:
First line contains 2 space-separated integers n and x respectively, which denote
n - Size of the array
x - Window size
Output:
A single integer denoting the max sum the player can obtain.
Working Soln Link: Ideone
long long solve(int n, int x) {
if (n == 0) return 0;
long long total = accumulate(arr + 1, arr + n + 1, 0ll);
if (x >= n) return total;
multiset<long long> dp_x;
for (int i = 1; i <= x + 1; i++) {
dp[i] = arr[i];
dp_x.insert(dp[i]);
}
for (int i = x + 2; i <= n; i++) {
dp[i] = arr[i] + *dp_x.begin();
dp_x.erase(dp_x.find(dp[i - x - 1]));
dp_x.insert(dp[i]);
}
long long ans = total;
for (int i = n - x; i <= n; i++) {
ans = min(ans, dp[i]);
}
return total - ans;
}
Can someone kindly explain how this code is working i.e., how line no. 12-26 in the Ideone solution is producing the correct answer?
I have dry run the code using pen and paper and found that it's giving the correct answer but couldn't figure out the algorithm used(if any). Can someone kindly explain to me how Line No. 12-26 is producing the correct answer? Is there any technique or algorithm at use here?
I am new to DP, so if someone can point out a tutorial(YouTube video, etc) related to this kind of problem, that would be great too. Thank you.
It looks like the idea is converting the problem - You must choose at least one coin in no more than x+1 coins in a row, and make it minimal. Then the original problem's answer would just be [sum of all values] - [answer of the new problem].
Then we're ready to talk about dynamic programming. Let's define a recurrence relation for f(i) which means "the partial answer of the new problem considering 1st to i-th coins, and i-th coin is chosen". (Sorry about the bad description, edits welcome)
f(i) = a(i) : if (i<=x+1)
f(i) = a(i) + min(f(i-1),f(i-2),...,f(i-x-1)) : otherwise
where a(i) is the i-th coin value
I added some comments line by line.
// NOTE f() is dp[] and a() is arr[]
long long solve(int n, int x) {
if (n == 0) return 0;
long long total = accumulate(arr + 1, arr + n + 1, 0ll); // get the sum
if (x >= n) return total;
multiset<long long> dp_x; // A min-heap (with fast random access)
for (int i = 1; i <= x + 1; i++) { // For 1 to (x+1)th,
dp[i] = arr[i]; // f(i) = a(i)
dp_x.insert(dp[i]); // Push the value to the heap
}
for (int i = x + 2; i <= n; i++) { // For the rest,
dp[i] = arr[i] + *dp_x.begin(); // f(i) = a(i) + min(...)
dp_x.erase(dp_x.find(dp[i - x - 1])); // Erase the oldest one from the heap
dp_x.insert(dp[i]); // Push the value to the heap, so it keeps the latest x+1 elements
}
long long ans = total;
for (int i = n - x; i <= n; i++) { // Find minimum of dp[] (among candidate answers)
ans = min(ans, dp[i]);
}
return total - ans;
}
Please also note that multiset is used as a min-heap. However we also need quick random-access(to erase the old ones) and multiset can do it in logarithmic time. So, the overall time complexity is O(n log x).
I am solving a problem which states that we have a list L containing integers from 1 to N. We have to perform the following operation N−1 times:
Choose two elements of the list, let's denote them by X and Y.
Erase the chosen elements from L.
Append the number X + Y + X*Y to L.
At the end, L contains exactly one integer. Find this integer.
As the answer may be large, we have to compute it modulo 10^9 + 7
Constraints :
1≤N≤1,000,000
Time Limit :
1 sec
I have written this code which gives the correct answer in linear time but it says time limit exceeded for this approach. Can someone provide a better optimized solution
inline ull cal(ull x, ull y){
ull ans, i, modno;
modno = 1000000007;
i = 1;
ans = (x + y);
i = (i*x) % modno;
i = (i*y) % modno;
ans = ans + i;
ans = ans % modno;
return ans;
}
int main(){
ull n;
cin>>n;
ull sum, modno;
sum = 0;
modno = 1000000007;
if(n == 1)
cout<<1<<endl;
else
{
sum = n + (n-1) + (n*(n-1));
n -= 2;
do
{
if(n <= 0)
break;
sum = cal(sum, n);
n -= 1;
}while(1);
cout<<ans<<endl;
}
return 0;
}
Final code :
ull n;
cin>>n;
if(n == 1)
cout<<1<<endl;
else
{
ull modno = 1000000007;
ull ans = 1;
ull no = n+1;
while(no >= 1)
{
ans = (ans*no);
if(ans > modno)
ans = ans%modno;
no--;
}
ans = ans - 1;
ans = ans % modno;
cout<<ans<<endl;
There's a closed-form solution for the sum: L = (N+1)!-1
The sum follows this recurrent equation L_N = N + L_(n-1) + N*L_(n-1), L_0=0 which can be obtained by simply always choosing X=L_(N-1) and Y=N ( = the next number to add).
Derivation:
EDIT:
As you posted your final code, I'm posting my benchmark:
#include <iostream>
#include <cstdint>
#include <chrono>
std::uint64_t
factorial(std::uint64_t n) {
std::uint64_t x = 1;
while (n > 1)
x = (x * n--) % 1'000'000'007;
return x;
}
int
main() {
std::uint64_t n;
std::cin >> n;
std::uint64_t numMicro = 0;
for (std::size_t i = 0; i < 1'000; ++i) {
auto start = std::chrono::high_resolution_clock::now();
volatile std::uint64_t res = factorial(n);
auto end = std::chrono::high_resolution_clock::now();
numMicro +=
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count();
}
std::cout << "On average: " << numMicro / 1000.0 << "microseconds";
return 0;
}
Compiled with -O3, volatile is there only to make sure that the compiler does not optimize the computation away.
Your solution is almost the same, way below the 1 second. Not sure what to optimize further.
As others have mentioned, the problem boils down to calculating ((n + 1)! - 1) % p. You can search around about fast methods of doing this (fast factorial modulo prime). One of those that would work under 1s is the one mentioned here
Update: Just checked the problem link from codechef. As usual, the trick lies in the constraints which you haven´t accurately described. You have to do the same task for up to 100000 cases. A single fact(n) mod p can be obtained in under 1 second using standard for loop, as n is small.
What won´t work is calculate fact(n) mod p for every test case. Like many other problems, you can benefit using precomputation: build an array where arr[i] is i! mod p up to i = max value n can take + 1. With this information, you can answer each query (test case) in O(1) by just returning (arr[n + 1] - 1) % p.
Just tried this and got accepted. Next time, please add problem link to your description, it is usually the case that you don´t think something is relevant and that part is the whole answer to the problem.
The algorithm should look like this:
sum <- 1
for index <- 2,n
sum = (sum + index + sum * index) mod 1000000007
end for
Explanation: since + and * are commutative and associative, the order in which the items are handled is irrelevant, so you are doing a good job implementing this cycle, but you unnecessarily overcomplicate your cal function.
The other answers tell you to calculate ((n + 1)! - 1) mod modno, which is correct if we forget about the modulo part, but I doubt that calculating ((n + 1)! - 1) mod modno will yield the very same result as computing this in a step-by-step manner regardless of the value of n, because we have + and * in each step. If the other answerers are correct, then you can greatly optimize your algorithm. If not, then optimizing this is not as easy.
The problem just says "Choose two elements of the list, let's denote them by X and Y." and doesn't say anything about the order that the elements need to be chosen.
Therefore it could be rewritten as:
Split the list into one sub-list per CPU
Using SIMD; calculate (X+1)*(Y+1) for each pair in each CPU's
sub-list and store the results in an new list as 64-bit integers so
that you can avoid doing the expensive modulo operation
Using SIMD; calculate (X*Y - 1) % 1000000007 for each pair in
each CPU's new sub-list and store the results as 32-bit integers.
Repeat the previous 2 steps until you're left with one value from
each CPU (and do the final R = (R - 1) % 1000000007 if necessary to bring it back to 32-bit). Store these
values in a list and terminate all threads except for one.
Using SIMD; calculate (X+1)*(Y+1) for each pair
Using SIMD; calculate (X+*Y - 1) % 1000000007 for each pair
Repeat the previous 2 steps until you're left with one value
I am having trouble understanding one of a Leetcode Problem.
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
Solution:
int numSquares(int n) {
static vector<int> dp {0};
while (dp.size() <= n) {
int m = dp.size(), squares = INT_MAX;
for (int i=1; i*i<=m; ++i)
squares = min(squares, dp[m-i*i] + 1);
dp.push_back(squares);
}
return dp[n];
}
I really dont understand what is going on with min(squares,dp[m-i*i]+1). Can you please explain?
thx.
I had a hard time with this too. Let's take the example number n=13.
First thing to observe is that: 1^2 =1, 2^2=4, 3^2=9, 4^2=16
So 13 can't be composed of anything greater than
3^2. Generically speaking, n can only be composed of numbers 1 to sqrt(n)
So we are left with some combination of the square of the following numbers: 1, 2, or 3.
Next thing we want to do is come up with the recursive formula. This took me a long time to understand. But we basically want to dwindle down to work with a smaller n (that's the whole point of recursion). We do that by subtracting our candidate perfect squares from n. For example:
If we try 3, then dp(13)=dp(13-3^2)+1=dp(4)+1.
The +1 is incrementing the count by 1 and is from the the fact that we already took off a perfect square from 13, which was the 3^2. Each +1 is a perfect square that we took off.
If we try 2, then dp(13)=13-2^2=dp(9)+1
If we try 1, then dp(13)=13-1^2=dp(12)+1
So we are left with comparing which is the smallest out of dp(4), dp(9), and dp(12). Hence the min.
The solution, which you have mentioned, is the bottom-up version of the algorithm. In order to understand the algorithm better, I would advice to look at the top-down version of the solution.
Let's look closer at the recurrence relation for the calculation of the minimal amount of the perfect squares, contained inside the number N. For given N and any arbitrary number x (which is the candidate for being considered as the member of the shortest sequence of numbers, whose perfect squares sums-up to N):
f(N, x) = 0 , if N = 0 ;
f(N, x) = min( f(N, x + 1), f(N - x^2, 1) ) , if N >= x^2 ;
f(N, x) = +infinity , otherwise ;
solution(N) = f(N, 1)
Now, having in mind the considered recurrence, we can construct the top-down solution (I will implement it in Java):
int solve(int n) {
return solve(n, 1);
}
int solve(int n, int curr) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
// if curr belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int inclusive = solve(n - (curr * curr), 1) + 1;
// otherwise:
int exclusive = solve(n, curr + 1);
return Math.min(exclusive, inclusive);
}
The runtime complexity of the given solution is exponential.
However, we can notice that there are only [1..n] possible values of n and [1..sqrt(n)] values of curr. Which, implies, that there are only n * sqrt(n) combinations of different values of arguments of the function solve. Hence, we can create the memoization table and reduce the complexity of the top-down solution:
int solve(int n) {
// initialization of the memoization table
int[][] memoized = new int[n + 1][(int) (Math.sqrt(n) + 1)];
for (int[] row : memoized) {
Arrays.fill(row, NOT_INITIALIZED);
}
return solve(n, 1, memoized);
}
int solve(int n, int curr, int[][] memoized) {
if (n == 0) {
return 0;
}
if ((curr * curr) > n) {
return POSITIVE_INFINITY;
}
if (memoized[n][curr] != NOT_INITIALIZED) {
// the sub-problem has been already solved
return memoized[n][curr];
}
int exclusive = solve(n, curr + 1, memoized);
int inclusive = solve(n - (curr * curr), 1, memoized) + 1;
memoized[n][curr] = Math.min(exclusive, inclusive);
return memoized[n][curr];
}
Given solution has the runtime complexity O(N * sqrt(N)).
However, it is possible to reduce the runtime complexity to O(N).
As far as the recurrence relation for f(N, x) depends only on f(N, x + 1) and f(N - x^2, 1) - it means, that the relation can be equivalently transformed to the loop form:
f(0) = 0
f(N) = min( f(N - x^2) + 1 ) , across the all x, such that x^2 <= N
In this case we have to memoize the f(N) only for N different values of its argument.
Hence, below presented the O(N) top-down solution:
int solve_top_down_2(int n) {
int[] memoized = new int[n + 1];
Arrays.fill(memoized, NOT_INITIALIZED);
return solve_top_down_2(n, memoized);
}
int solve_top_down_2(int n, int[] memoized) {
if (n == 0) {
return 0;
}
if (memoized[n] != NOT_INITIALIZED) {
return memoized[n];
}
// if 1 belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
int result = solve_top_down_2(n - (1 * 1)) + 1;
for (int curr = 2; (curr * curr) <= n; curr++) {
// check, whether some other number belongs to the shortest sequence of numbers, whose perfect squares sums-up to N
result = Math.min(result, solve_top_down_2(n - (curr * curr)) + 1);
}
memoized[n] = result;
return result;
}
Finally, the presented top-down solution can be easily transformed to the bottom-up solution:
int solve_bottom_up(int n) {
int[] memoized = new int[n + 1];
for (int i = 1; i <= n; i++) {
memoized[i] = memoized[i - (1 * 1)] + 1;
for (int curr = 2; (curr * curr) <= i; curr++) {
memoized[i] = Math.min(memoized[i], memoized[i - (curr * curr)] + 1);
}
}
return memoized[n];
}
The clarification to your confusion lies in the question itself. The structure dp holds the least number of squares that sum up to the index position of dp.
E.g., squares would return 3 when n=9, but least possible is 1, which is what dp[m- i*i] + 1 would return.