Will memoization work for all DP problems? [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I was solving this famous DP problem (ROD cutting) Problem Link
I understood the approach given in the editorials, but I came up with my own recursive solution..
that actually worked but the time limit is exceeding, so when I tried memoizing the same, I'm getting different answers
Code:
#include <bits/stdc++.h>
using namespace std;
// Approach 1:
// Recursion
int helper1(vector<int> &price, int size, int index, int profit)
{
if (size == 0)
return profit;
if (size < 0 or index == price.size())
return INT_MIN;
return max(helper1(price, size - (index + 1), index, profit + price[index]),
helper1(price, size, index + 1, profit));
}
int cutRod1(vector<int> &price)
{
int n = price.size();
return helper1(price, n, 0, 0);
}
// Approach 2:
// Recursion + Memoization
int helper2(vector<int> &price, int size, int index, int profit, vector<vector<int>> &dp)
{
if (size == 0)
return profit;
if (size < 0 or index == price.size())
return INT_MIN;
if (dp[index][size] != -1)
return dp[index][size];
return dp[index][size] = max(helper2(price, size - (index + 1), index, profit + price[index], dp),
helper2(price, size, index + 1, profit, dp));
}
int cutRod2(vector<int> &price)
{
int n = price.size();
vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
return helper2(price, n, 0, 0, dp);
}
void solve()
{
vector<int> price{3, 9, 13, 12, 8, 12, 8, 8, 3, 10, 13};
cout << cutRod1(price) << endl;
cout << cutRod2(price) << endl;
}
int main()
{
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t{1};
// cin >> t;
while (t--)
solve();
return 0;
}
Recursive solution's result: 49 (Correct)
Memoized solutions's result: 48 (Wrong)
Intuition behind my approach:
It is an unbounded Knapsack problem, we have two choices:
Take a cut at index's position and stay there (for making furthur cuts at the same position)
Not take a cut at index's position, move to next index (index+1)
can anyone help me finding out the reason why memoized version doesn't work

You need to include profit in the memoization lookup. At some point, you're calling helper with the same (size, index) pair but the passed in profit value is different.

This is a solution with memoization. The source contains a complete explanation. Source
#include <iostream>
#include<algorithm> // To use the built in max function
using namespace std;
int main() {
// Suppose that we have a rod of length 5, and an array containing the
// length(1,2,3 and 4 ) and price(2,5,7 and 8 ) of the pieces.
int price[] = {2,5,7,8};
int n = 5;
// Declaring a 2D array, T
int T[n-1][n+1];
// Initializing the array to all zeros
for(int i=0; i < n-1; i++)
{
for(int j=0; j < n+1; j++ )
{
T[i][j] = 0;
}
}
for(int i=0; i < n-1; i++)
{
for(int j=0; j < n+1; j++ )
{
// First column => 0 length of rod => 0 profit
if(j == 0) {
continue;
}
// First row => T[i-1][j] doesn't exist so just pick the second value
else if(i == 0) {
T[i][j] = price[i] + T[i][j-i-1];
}
// where j <= i => T[i][j-i-1] doesn't exist so just pick the first value
else if(j-i-1 < 0) {
T[i][j] = T[i-1][j];
}
// using the whole expression
else {
T[i][j] = std::max(T[i-1][j], (price[i] + T[i][j-i-1]));
}
}
}
// Answer in the extreme bottom right cell
cout << "Maximum profit is " << T[n-2][n] << endl;
return 0;
}

Related

How to find all possible combinations of adding two variables, each attached to a multiplier, summing up to a given number (cin)?

In my situation, a lorry has a capacity of 30, while a van has a capacity of 10. I need to find the number of vans/lorries needed to transport a given amount of cargo, say 100. I need to find all possible combinations of lorries + vans that will add up to 100.
The basic math calculation would be: (30*lorrycount) + (10*vancount) = n, where n is number of cargo.
Output Example
Cargo to be transported: 100
Number of Lorry: 0 3 2 1
Number of Van: 10 1 4 7
For example, the 2nd combination is 3 lorries, 1 van. Considering that lorries have capacity = 30 and van capacity = 10, (30*3)+(10*1) = 100 = n.
For now, we only have this code, which finds literally all combinations of numbers that add up to given number n, without considering the formula given above.
#include <iostream>
#include <vector>
using namespace std;
void findCombinationsUtil(int arr[], int index,
int num, int reducedNum)
{
int lorry_capacity = 30;
int van_capacity = 10;
// Base condition
if (reducedNum < 0)
return;
// If combination is found, print it
if (reducedNum == 0)
{
for (int i = 0; i < index; i++)
cout << arr[i] << " ";
cout << endl;
return;
}
// Find the previous number stored in arr[]
// It helps in maintaining increasing order
int prev = (index == 0) ? 1 : arr[index - 1];
// note loop starts from previous number
// i.e. at array location index - 1
for (int k = prev; k <= num; k++)
{
// next element of array is k
arr[index] = k;
// call recursively with reduced number
findCombinationsUtil(arr, index + 1, num,
reducedNum - k);
}
}
void findCombinations(int n)
{
// array to store the combinations
// It can contain max n elements
std::vector<int> arr(n); // allocate n elements
//find all combinations
findCombinationsUtil(&*arr.begin(), 0, n, n);
}
int main()
{
int n;
cout << "Enter the amount of cargo you want to transport: ";
cin >> n;
cout << endl;
//const int n = 10;
findCombinations(n);
return 0;
}
Do let me know if you have any solution to this, thank you.
An iterative way of finding all possible combinations
#include <iostream>
#include <vector>
int main()
{
int cw = 100;
int lw = 30, vw = 10;
int maxl = cw/lw; // maximum no. of lorries that can be there
std::vector<std::pair<int,int>> solutions;
// for the inclusive range of 0 to maxl, find the corresponding no. of vans for each variant of no of lorries
for(int l = 0; l<= maxl; ++l){
bool is_integer = (cw - l*lw)%vw == 0; // only if this is true, then there is an integer which satisfies for given l
if(is_integer){
int v = (cw-l*lw)/vw; // no of vans
solutions.push_back(std::make_pair(l,v));
}
}
for( auto& solution : solutions){
std::cout<<solution.first<<" lorries and "<< solution.second<<" vans" <<std::endl;
}
return 0;
}
We will create a recursive function that walks a global capacities array left to right and tries to load cargo into the various vehicle types. We keep track of how much we still have to load and pass that on to any recursive call. If we reach the end of the array, we produce a solution only if the remaining cargo is zero.
std::vector<int> capacities = { 30, 10 };
using Solution = std::vector<int>;
using Solutions = std::vector<Solution>;
void tryLoad(int remaining_cargo, int vehicle_index, Solution so_far, std::back_insert_iterator<Solutions>& solutions) {
if (vehicle_index == capacities.size()) {
if (remaining_cargo == 0) // we have a solution
*solutions++ = so_far;
return;
}
int capacity = capacities[vehicle_index];
for (int vehicles = 0; vehicles <= remaining_cargo / capacity; vehicles++) {
Solution new_solution = so_far;
new_solution.push_back(vehicles);
tryLoad(remaining_cargo - vehicles * capacity, vehicle_index + 1, new_solution, solutions);
}
}
Calling this as follows should produce the desired output in all_solutions:
Solutions all_solutions;
auto inserter = std::back_inserter(all_solutions)
tryLoad(100, 0, Solution{}, inserter);

How to produce random numbers so that their sum is equal to given number?

I want to produce X random numbers, each from the interval <0; Y> (given Y as a maximum of each number), but there is restriction that the sum of these numbers must be equal to Z.
Example:
5 Randoms numbers, each max 6 and the sum must be equal to 14, e.g: 0, 2, 6, 4, 2
Is there already a C/C++ function that could do something like that?
Personally I couldn't come up with more than some ugly if-else-constucts.
Since you don't need the generated sequence to be uniform, this could be one of the possible solutions:
#include <iostream>
#include <vector>
#include <cstdlib>
int irand(int min, int max) {
return ((double)rand() / ((double)RAND_MAX + 1.0)) * (max - min + 1) + min;
}
int main()
{
int COUNT = 5, // X
MAX_VAL = 6, // Y
MAX_SUM = 14; // Z
std::vector<int> buckets(COUNT, 0);
srand(time(0));
int remaining = MAX_SUM;
while (remaining > 0)
{
int rndBucketIdx = irand(0, COUNT-1);
if (buckets[rndBucketIdx] == MAX_VAL)
continue; // this bucket is already full
buckets[rndBucketIdx]++;
remaining--;
}
std::cout << "Printing sequence: ";
for (size_t i = 0; i < COUNT; ++i)
std::cout << buckets[i] << ' ';
}
which just simply divides the total sum to bunch of buckets until it's gone :)
Example of output: Printing sequence: 4 4 1 0 5
NOTE: this solution was written when the question specified a "MAX SUM" parameter, implying a sum of less than that amount was equally acceptable. The question's now been edited based on the OP's comment that they meant the cumulative sum must actually hit that target. I'm not going to update this answer, but clearly it could trivially discard lesser totals at the last level of recursion.
This solution does a one-time population of a vector<vector<int>> with all the possible combinations of numbers solving the input criterion, then each time a new solution is wanted it picks one of those at random and shuffles the numbers into a random order (thereby picking a permutation of the combination).
It's a bit heavy weight - perhaps not suitable for the actual use that you mentioned after I'd started writing it ;-P - but it produces an even-weighted distribution, and you can easily do things like guarantee a combination won't be returned again until all other combinations have been returned (with a supporting shuffled vector of indices into the combinations).
#include <iostream>
#include <vector>
#include <algorithm>
using std::min;
using std::max;
using std::vector;
// print solutions...
void p(const vector<vector<int>>& vvi)
{
for (int i = 0; i < vvi.size(); ++i)
{
for (int j = 0; j < vvi[i].size(); ++j)
std::cout << vvi[i][j] << ' ';
std::cout << '\n';
}
}
// populate results with solutions...
void f(vector<vector<int>>& results, int n, int max_each, int max_total)
{
if (n == 0) return;
if (results.size() == 0)
{
for (int i = 0; i <= min(max_each, max_total); ++i)
results.push_back(vector<int>(2, i));
f(results, n - 1, max_each, max_total);
return;
}
vector<vector<int>> new_results;
for (int r = 0; r < results.size(); ++r)
{
int previous = *(results[r].rbegin() + 1);
int current_total = results[r].back();
int remaining = max_total - current_total;
for (int i = 0; i <= min(previous,min(max_each, remaining)); ++i)
{
vector<int> v = results[r];
v.back() = i;
v.push_back(current_total + i);
new_results.push_back(v);
}
}
results = new_results;
f(results, n - 1, max_each, max_total);
}
const vector<int>& once(vector<vector<int>>& solutions)
{
int which = std::rand() % solutions.size();
vector<int>& v = solutions[which];
std::random_shuffle(v.begin(), v.end() - 1);
return v;
}
int main()
{
vector<vector<int>> solutions;
f(solutions, 5, 6, 14);
std::cout << "All solution combinations...\n";
p(solutions);
std::cout << "------------------\n";
std::cout << "A few sample permutations...\n";
for (int n = 1; n <= 100; ++n)
{
const vector<int>& o = once(solutions);
for (int i = 0; i < o.size() - 1; ++i)
std::cout << o[i] << ' ';
std::cout << '\n';
}
}
#include<iostream>
#include <cstdlib> //rand ()
using namespace std;
void main()
{
int random ,x=5;
int max , totalMax=0 , sum=0;
cout<<"Enter the total maximum number : ";
cin>>totalMax;
cout<<"Enter the maximum number: ";
cin>>max;
srand(0);
for( int i=0; i<x ; i++)
{
random=rand()%max+1; //range from 0 to max
sum+=random;
if(sum>=totalMax)
{
sum-=random;
i--;
}
else
cout<<random<<' ';
}
cout<<endl<<"Reached total maximum number "<<totalMax<<endl;
}
I wrote this simple code
I tested it using totalMax=14 and max=3 and it worked with me
hope it's what you asked for
LiHo's answer looks pretty similar to my second suggestion, so I'll leave that, but here's an example of the first. It could probably be improved, but it shouldn't have any tragic bugs. Here's a live sample.
#include <algorithm>
#include <array>
#include <random>
std::random_device rd;
std::mt19937 gen(rd());
constexpr int MAX = 14;
constexpr int LINES = 5;
int sum{};
int maxNum = 6;
int minNum{};
std::array<int, LINES> nums;
for (int i = 0; i < LINES; ++i) {
maxNum = std::min(maxNum, MAX - sum);
// e.g., after 0 0, min is 2 because only 12/14 can be filled after
int maxAfterThis = maxNum * (LINES - i - 1);
minNum = std::min(maxNum, std::max(minNum, MAX - sum - maxAfterThis));
std::uniform_int_distribution<> dist(minNum, maxNum);
int num = dist(gen);
nums[i] = num;
sum += num;
}
std::shuffle(std::begin(nums), std::end(nums), gen);
Creating that ditribution every time could potentially slow it down (I don't know), but the range has to go in the constructor, and I'm not one to say how well distributed these numbers are. However, the logic is pretty simple. Aside from that, it uses the nice, shiny C++11 <random> header.
We just make sure no remaining number goes over MAX (14) and that MAX is reached by the end. minNum is the odd part, and that's due to how it progresses. It starts at zero and works its way up as needed (the second part to std::max is figuring out what would be needed if we got 6s for the rest), but we can't let it surpass maxNum. I'm open to a simpler method of calculating minNum if it exists.
Since you know how many numbers you need, generate them from the given distribution but without further conditions, store them, compute the actual sum, and scale them all up/down to get the desired sum.

Find minimal length of sub-array whose sum is greater than K [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions must demonstrate a minimal understanding of the problem being solved. Tell us what you've tried to do, why it didn't work, and how it should work. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
How can I find sub-array qualifying that its sum is greater than a given K?
What I came up with is maintain to pointer at the start and end of the sequence, and incrementally subtract the smaller one to shorten the sequence. But seems it's invalid. Why?
Here is my implmentation:
#include <iostream>
using namespace std;
int main() {
while (!cin.eof()) {
int caseCount;
cin >> caseCount;
int N, S;
for (int i = 0; i < caseCount; i++) {
cin >> N >> S;
int * seq = new int[N];
int maxSum = 0;
for (int j = 0; j < N; j ++) {
cin >> seq[j];
maxSum += seq[j];
}
if (maxSum < S) {
cout << 0 << endl;
continue;
}
int left, right;
left = 0;
right = N-1;
while(left < right) {
if(seq[left] < seq[right]) {
if (maxSum - seq[left] < S) {
cout << right-left+1 << endl;
break;
} else {
maxSum -= seq[left];
left++;
}
} else {
if (maxSum - seq[right] < S) {
cout << right-left+1 << endl;
break;
} else {
maxSum -= seq[right];
right--;
}
}
}
if (left >= right) {
cout << 1 << endl;
}
}
}
return 0;
}
Sample Input:
2 // amount of sequences to input
10 15 // sequence 1 length and K
5 1 3 5 10 7 4 9 2 8 // sequence 1 data
5 11 // sequence 2 length and K
1 2 3 4 5 // sequence 2 data
Sample Output:
2
3
Here is working example in C++ based on Thijs algorithm, which seems like the ideal algorithm for your problem (if we understood it correctly. It can be changed easily to find the first subsequence or all subsequences matching the predicate)
#include <vector>
#include <utility>
#include <iostream>
using namespace std;
template<typename It>
pair<It, It> subseq_with_sum_greater(It begin, It end, typename It::value_type barrier)
{
typename It::value_type current_sum = 0;
pair<It, It> res = make_pair(begin, end);
for(It current = begin; current < end; ++current)
{
current_sum += *current;
while(current_sum > barrier and current_sum - *begin > barrier)
current_sum -= *begin++;
if(current_sum > barrier and distance(begin, current) < distance(res.first, res.second))
res = make_pair(begin, current);
}
return res;
}
int main()
{
vector<int> v = {5, 1, 3, 5, 10, 7, 4, 9, 2, 8};
auto subseq = subseq_with_sum_greater(v.begin(), v.end(), 15);
cout << distance(v.begin(), subseq.first) << ", " << distance(v.begin(), subseq.second);
}
And the output is 4, 5, the indexes of the subsequence. Please note that using std::distance is O(1) complexity only with RandomAccess iterators (like those on std::vector), you may want to add size_t current_distance, minimal_distance variables if you want to use this kind of template on other containers. Also, when not finding any subsequence, this algorithm returns a begin, end pair, which makes it difficult to know if this is the answer or if no subsequence match. depending on your case, you may want to have a more precise output.
Here's an idea in Python (since it's mostly an algorithmic question), assuming your inputs are natural numbers (thanks #ChrisOkasaki). It operates on lists, but it should be easy to adjust for your purpose. Both start and end are inclusive. It returns both the first and the last index of the sub-array.
def find_minimal_length_subarr(arr, min_sum):
found = False
start = end = cur_start = cur_end = 0
cur_sum = arr[cur_start]
while cur_end < len(arr):
if cur_start < cur_end:
cur_sum += arr[cur_end]
while cur_sum-arr[cur_start] >= min_sum:
cur_sum -= arr[cur_start]
cur_start += 1
if cur_sum >= min_sum and (not found or cur_end-cur_start < end-start):
start, end = cur_start, cur_end
found = True
cur_end += 1
if found:
return start, end
print find_minimal_length_subarr([11, 2, 3, 4, 9, 5, 6, 7, 8], 21) # (6, 8)
It starts from the beginning and expands to the right while the min_sum is not reached. When reached, it shortens from the left while min_sum is still reached. Then it continues to expand again. Only if a better (shorter) candidate is found, an earlier is replaced. Time complexity is O(n), space complexity O(1).
Your algorithm is incorrect. You basically only check middle of the sequence, which doesn't make any sense. Instead you should start with both indexes at the beginning of the array and increment right as long as sum of subrange is smaller than K. When it gets bigger start incrementing left until it is smaller again. Now you have a candidate for your shortest subsequence - save it. Repeat until right won't get past the end of array, updating your candidate if new one is shorter.
This will work with negative content too:
def s(arr, K):
candidate = None
for l,r in [(x,x+1) for x in range(len(arr))]:
while sum(arr[l:r]) < K and r <= len(arr): r = r + 1
if K <= sum(arr[l:r]):
if candidate is None or r-l < candidate[1]-candidate[0]:
candidate = (l,r)
return candidate # ending index will be exclusive
In C++:
typedef struct {
bool found; int l; int r;
} range;
int sum(int arr[], int arr_n, int l, int r) {
int sum = 0;
for (int i=l; i<r && i<arr_n; i++)
sum+=arr[i];
return sum;
}
range s(int arr[], int K, int arr_n) {
bool found = false; int c_l; int c_r;
for (int l=0; l<arr_n; l++) {
int r = l+1;
while (sum(arr, arr_n, l, r) < K && r <= arr_n) r++;
if (K <= sum(arr, arr_n, l, r))
if (!found || r-l < c_r-c_l) {
c_l = l; c_r = r; found = true;
}
}
return (range){found, c_l, c_r};
}

How to do memoisation in following c++ code

Actually i am trying to solve SPOJ Problem:
[SPOJ] http://www.spoj.com/problems/SQRBR/ . I came up with recurence to solve it but i am not getting how to do memoisation. Any suggestion on how to memoisation for given problem will be helpful. my code is giving correct answer , but it is giving TLE in spoj Here my code :
#include <iostream>
#include <cstdio>
using namespace std;
void balancedParen(int n, int open, int position, int close, char str[], string s, long long int &counter) {
if(close == n) {
str[pos] = '\0';
printf("%s\n", str);
counter++;
return;
}
if(s[position] == '(' ) {
if(open <= n-1) {
str[position] = '(';
balancedParen(n, open+1, position+1, close, str, s, counter);
}
} else {
if(open < n) {
str[position] = '(';
balancedParen(n, open+1, position+1, close, str, s, counter);
}
if(open > close) {
str[position] = ')';
balancedParen(n, open, position+1, close+1, str, s, counter);
}
}
return ;
}
int main() {
int a[100], n, k, i;
long long counter = 0;
int testCases;
scanf("%d", &testCases);
while(testCases--) {
scanf("%d", &n);
scanf("%d", &k);
char str[100];
string s = "..........................................................................";
for(i = 0; i < k; i++) {
scanf("%d", &a[i]);
s[a[i]-1] = '(';
}
balancedParen(n, 0, 0, 0, str, s, counter);
printf("%lld\n", counter);
counter = 0;
}
return 0;
}
I can think of one relatively simple and possibly significant optimization.
First, instead of making the "counter" a reference, make it the return value of the function. Hear me out for a bit here.
Now say the positions you're given are "1, 7, 15". Instead of recursively going "1, 2, 3, 4, 5, 6, 7", you can be a little tricky and go to 7 in one step.
You just need to count the number of permutations which can be used to go between 1 and 7, for every possible number of opening parens (in this case, 3, 4, 5 and 6)
For example, how many ways exist to have 3 opening parens between 1 and 7?
[[[]]]
[[][]]
[][][]
[[]][]
[][[]]
5 permutations (unless I missed one). So you can add 5*balancedParen(n, open+3, position+6, close+3, str, s, counter) to your result. And do a similar thing for 4, 5, and 6 opening parens.
Of course you'd need to write another function (recursive approach seems simplest) to find that number "5". But the advantage is that the total number of function calls is now (calls to get from 1 to 7) + (calls to get from 7 to 15), rather than (calls to get from 1 to 7) * (calls to get from 7 to 15).
Here is some code which should work using the algorithm I described:
int countPermutations(int unclosed, int length, int toOpen)
{
if (toOpen > length) // impossible to open this many, not enough length
return 0;
int toClose = length-toOpen;
if (toClose - toOpen > unclosed)
return 0; // No possibilities; not enough open parens to fill the length
if (toOpen == 0 || toOpen == length)
return 1; // Only one possibility now
int ret = 0;
if (toOpen > 0) // Count permutations if we opened a paren here
ret += countPermutations(unclosed+1, length-1, toOpen-1);
if (unclosed > 0) // Count permutations if we closed a paren here
ret += countPermutations(unclosed-1, length-1, toOpen);
return ret;
}
int countNLengthSolutions(int n, int unclosed, int position, int *positions, int remainingPositions)
{
if (n % 2 != 0)
return 0; // must be a length divisible by 2
if (position > n)
return 0;
if (n-position < unclosed)
return 0; // too many open parens, no way to complete within length
if (remainingPositions == 0)
{
// Too many open parens to close by the time we get to length N?
if ((n - position) < unclosed)
return 0;
else // Say we have 4 open and a length of 10 to fill; we want (10-4)/2 = 3 more open parens.
return countPermutations(unclosed, n-position, (n-position - unclosed)/2);
}
else
{
int ret = 0;
int toFill = *positions - position - 1;
for (int openParens = 0; openParens <= toFill; openParens++)
{
int permutations = countPermutations(unclosed, toFill, openParens);
if (permutations > 0)
ret += permutations*countNLengthSolutions(n, unclosed+(2*openParens-toFill)+1, position+toFill+1, positions+1, remainingPositions-1);
}
return ret;
}
}
I may have a bug somewhere, I didn't really spend the time to check, but I verified it works for all the sample input.

How can we find second maximum from array efficiently?

Is it possible to find the second maximum number from an array of integers by traversing the array only once?
As an example, I have a array of five integers from which I want to find second maximum number. Here is an attempt I gave in the interview:
#define MIN -1
int main()
{
int max=MIN,second_max=MIN;
int arr[6]={0,1,2,3,4,5};
for(int i=0;i<5;i++){
cout<<"::"<<arr[i];
}
for(int i=0;i<5;i++){
if(arr[i]>max){
second_max=max;
max=arr[i];
}
}
cout<<endl<<"Second Max:"<<second_max;
int i;
cin>>i;
return 0;
}
The interviewer, however, came up with the test case int arr[6]={5,4,3,2,1,0};, which prevents it from going to the if condition the second time.
I said to the interviewer that the only way would be to parse the array two times (two for loops). Does anybody have a better solution?
Your initialization of max and second_max to -1 is flawed. What if the array has values like {-2,-3,-4}?
What you can do instead is to take the first 2 elements of the array (assuming the array has at least 2 elements), compare them, assign the smaller one to second_max and the larger one to max:
if(arr[0] > arr[1]) {
second_max = arr[1];
max = arr[0];
} else {
second_max = arr[0];
max = arr[1];
}
Then start comparing from the 3rd element and update max and/or second_max as needed:
for(int i = 2; i < arr_len; i++){
// use >= n not just > as max and second_max can hav same value. Ex:{1,2,3,3}
if(arr[i] >= max){
second_max=max;
max=arr[i];
}
else if(arr[i] > second_max){
second_max=arr[i];
}
}
The easiest solution would be to use std::nth_element.
You need a second test:
for(int i=0;i<5;i++){
if(arr[i]>max){
second_max=max;
max=arr[i];
}
else if (arr[i] > second_max && arr[i] != max){
second_max = arr[i];
}
}
Your original code is okay, you just have to initialize the max and second_max variables. Use the first two elements in the array.
Here you are:
std::pair<int, int> GetTwoBiggestNumbers(const std::vector<int>& array)
{
std::pair<int, int> biggest;
biggest.first = std::max(array[0], array[1]); // Biggest of the first two.
biggest.second = std::min(array[0], array[1]); // Smallest of the first two.
// Continue with the third.
for(std::vector<int>::const_iterator it = array.begin() + 2;
it != array.end();
++it)
{
if(*it > biggest.first)
{
biggest.second = biggest.first;
biggest.first = *it;
}
else if(*it > biggest.second)
{
biggest.second = *it;
}
}
return biggest;
}
Quickselect is the way to go with this one. Pseudo code is available at that link so I shall just explain the overall algorithm:
QuickSelect for kth largest number:
Select a pivot element
Split array around pivot
If (k < new pivot index)
perform quickselect on left hand sub array
else if (k > new pivot index)
perform quickselect on right hand sub array (make sure to offset k by size of lefthand array + 1)
else
return pivot
This is quite obviously based on the good old quicksort algorithm.
Following this algorithm through, always selecting element zero as the pivot every time:
select 4th largest number:
1) array = {1, 3, 2, 7, 11, 0, -4}
partition with 1 as pivot
{0, -4, _1_, 3, 2, 7, 11}
4 > 2 (new pivot index) so...
2) Select 1st (4 - 3) largest number from right sub array
array = {3, 2, 7, 11}
partition with 3 as pivot
{2, _3_, 7, 11}
1 < 2 (new pivot index) so...
3) select 1st largest number from left sub array
array = {2}
4) Done, 4th largest number is 2
This will leave your array in an undefined order afterwards, it's up to you if that's a problem.
Step 1. Decide on first two numbers.
Step 2. Loop through remaining numbers.
Step 3. Maintain latest maximum and second maximum.
Step 4. When updating second maximum, be aware that you are not making maximum and second maximum equal.
Tested for sorted input (ascending and descending), random input, input having duplicates, works fine.
#include <iostream>
#define MAX 50
int GetSecondMaximum(int* data, unsigned int size)
{
int max, secmax;
// Decide on first two numbers
if (data[0] > data[1])
{
max = data[0];
secmax = data[1];
}
else
{
secmax = data[0];
max = data[1];
}
// Loop through remaining numbers
for (unsigned int i = 2; i < size; ++i)
{
if (data[i] > max)
{
secmax = max;
max = data[i];
}
else if (data[i] > secmax && data[i] != max/*removes duplicate problem*/)
secmax = data[i];
}
return secmax;
}
int main()
{
int data[MAX];
// Fill with random integers
for (unsigned int i = 0; i < MAX; ++i)
{
data[i] = rand() % MAX;
std::cout << "[" << data[i] << "] "; // Display input
}
std::cout << std::endl << std::endl;
// Find second maximum
int nSecondMax = GetSecondMaximum(data, MAX);
// Display output
std::cout << "Second Maximum = " << nSecondMax << std::endl;
// Wait for user input
std::cin.get();
return 0;
}
Other way to solve this problem, is to use comparisons among the elements. Like for example,
a[10] = {1,2,3,4,5,6,7,8,9,10}
Compare 1,2 and say max = 2 and second max = 1
Now compare 3 and 4 and compare the greatest of them with max.
if element > max
second max = max
element = max
else if element > second max
second max = element
The advantage with this is, you are eliminating two numbers in just two comparisons.
Let me know, if you have any problem understanding this.
Check this solution.
max1 = a[0];
max2 = a[1];
for (i = 1; i < n; i++)
{
if (max1 < a[i])
{
max2 = max1;
max1 = a[i];
}
if (max2 == max1) max2 = a[i + 1];
if (max2 == a[n])
{
printf("All numbers are the same no second max.\n");
return 0;
}
if (max2 < a[i] && max1 != a[i]) max2 = a[i];
}
Here is something which may work ,
public static int secondLargest(int[] a){
int max=0;
int secondMax=0;
for(int i=0;i<a.length;i++){
if(a[i]<max){
if(a[i]>secondMax){
secondMax=a[i];
}
continue;
}
if(a[i]>max){
secondMax=max;
max=a[i];
}
}
return secondMax;
}
The upper bound should have be n+log2⁡n−2, but it bigger than O(n) in case of random selection algorithm, but in worst case it much smaller. The solution might be
build a tree like to find the MAX element with n - 1 comparisons
max(N)
/ \
max(N/2) max(N/2)
remove the MAX and find the MAX again log2n - 1 comparison
PS. It uses additional memory, but it faster than random selection algorithm in worst case.
Can't we just sort this in decreasing order and take the 2nd element from the sorted array?
How about the following below.
make_heap is O(n) so this is efficient and this is 1-pass
We find the second max by taking advantage that it must be one of the heap children of the parent, which had the maximum.
#include <algorithm>
#include <iostream>
int main()
{
int arr[6]={0,1,2,3,4,5};
std::make_heap(arr, arr+6);
std::cout << "First Max: " << arr[0] << '\n';
std::cout << "Second Max: " << std::max(arr[1], arr[2]) << '\n';
return 0;
}
int max,secondMax;
max=secondMax=array[0];
for(int i=0;i<array.length;i++)
{ if(array[i]>max) { max=array[i]; }
if(array[i]>secondMax && array[i]<max) {
secondMax=array[i]; }
}
#include <iostream>
using namespace std;
int main() {
int max = 0;
int sec_Max = 0;
int array[] = {81,70,6,78,54,77,7,78};
int loopcount = sizeof(array)/sizeof(int);
for(int i = 0 ; i < loopcount ; ++i)
{
if(array[i]>max)
{
sec_Max = max;
max = array[i];
}
if(array[i] > sec_Max && array[i] < max)
{
sec_Max = array[i];
}
}
cout<<"Max:" << max << " Second Max: "<<sec_Max<<endl;
return 0;
}
// Set the first two different numbers as the maximum and second maximum numbers
int max = array[0];
int i = 1;
//n is the amount of numbers
while (array[i] == max && i < n) i++;
int sec_max = array[i];
if( max < sec_max ) {
tmp = sec_max;
sec_max = max;
max = tmp;
}
//find the second maximum number
for( ; i < n; ++i ) {
if( array[i] > max ) {
sec_max = max;
max = array[i];
} else if( array[i] > sec_max && array[i] != max ) {
sec_max = array[i];
}
}
printf("The second maximum number is %d\n", sec_max);