I've just started learning Backtracking algorithms at college. Somehow I've managed to make a program for the Subset-Sum problem. Works fine but then i discovered that my program doesn't give out all the possible combinations.
For example : There might be a hundred combinations to a target sum but my program gives only 30.
Here is the code. It would be a great help if anyone could point out what my mistake is.
int tot=0;//tot is the total sum of all the numbers in the set.
int prob[500], d, s[100], top = -1, n; // n = number of elements in the set. prob[i] is the array with the set.
void subset()
{
int i=0,sum=0; //sum - being updated at every iteration and check if it matches 'd'
while(i<n)
{
if((sum+prob[i] <= d)&&(prob[i] <= d))
{
s[++top] = i;
sum+=prob[i];
}
if(sum == d) // d is the target sum
{
show(); // this function just displays the integer array 's'
top = -1; // top points to the recent number added to the int array 's'
i = s[top+1];
sum = 0;
}
i++;
while(i == n && top!=-1)
{
sum-=prob[s[top]];
i = s[top--]+1;
}
}
}
int main()
{
cout<<"Enter number of elements : ";cin>>n;
cout<<"Enter required sum : ";cin>>d;
cout<<"Enter SET :\n";
for(int i=0;i<n;i++)
{
cin>>prob[i];
tot+=prob[i];
}
if(d <= tot)
{
subset();
}
return 0;
}
When I run the program :
Enter number of elements : 7
Enter the required sum : 12
Enter SET :
4 3 2 6 8 12 21
SOLUTION 1 : 4, 2, 6
SOLUTION 2 : 12
Although 4, 8 is also a solution, my program doesnt show it.
Its even worse with the number of inputs as 100 or more. There will be atleast 10000 combinations, but my program shows 100.
The Logic which I am trying to follow :
Take in the elements of the main SET into a subset as long as the
sum of the subset remains less than or equal to the target sum.
If the addition of a particular number to the subset sum makes it
larger than the target, it doesnt take it.
Once it reaches the end
of the set, and answer has not been found, it removes the most
recently taken number from the set and starts looking at the numbers
in the position after the position of the recent number removed.
(since what i store in the array 's' is the positions of the
selected numbers from the main SET).
The solutions you are going to find depend on the order of the entries in the set due to your "as long as" clause in step 1.
If you take entries as long as they don't get you over the target, once you've taken e.g. '4' and '2', '8' will take you over the target, so as long as '2' is in your set before '8', you'll never get a subset with '4' and '8'.
You should either add a possibility to skip adding an entry (or add it to one subset but not to another) or change the order of your set and re-examine it.
It may be that a stack-free solution is possible, but the usual (and generally easiest!) way to implement backtracking algorithms is through recursion, e.g.:
int i = 0, n; // i needs to be visible to show()
int s[100];
// Considering only the subset of prob[] values whose indexes are >= start,
// print all subsets that sum to total.
void new_subsets(int start, int total) {
if (total == 0) show(); // total == 0 means we already have a solution
// Look for the next number that could fit
while (start < n && prob[start] > total) {
++start;
}
if (start < n) {
// We found a number, prob[start], that can be added without overflow.
// Try including it by solving the subproblem that results.
s[i++] = start;
new_subsets(start + 1, total - prob[start]);
i--;
// Now try excluding it by solving the subproblem that results.
new_subsets(start + 1, total);
}
}
You would then call this from main() with new_subsets(0, d);. Recursion can be tricky to understand at first, but it's important to get your head around it -- try easier problems (e.g. generating Fibonacci numbers recursively) if the above doesn't make any sense.
Working instead with the solution you have given, one problem I can see is that as soon as you find a solution, you wipe it out and start looking for a new solution from the number to the right of the first number that was included in this solution (top = -1; i = s[top+1]; implies i = s[0], and there is a subsequent i++;). This will miss solutions that begin with the same first number. You should just do if (sum == d) { show(); } instead, to make sure you get them all.
I initially found your inner while loop pretty confusing, but I think it's actually doing the right thing: once i hits the end of the array, it will delete the last number added to the partial solution, and if this number was the last number in the array, it will loop again to delete the second-to-last number from the partial solution. It can never loop more than twice because numbers included in a partial solution are all at distinct positions.
I haven't analysed the algorithm in detail, but what struck me is that your algorithm doesn't account for the possibility that, after having one solution that starts with number X, there could be multiple solutions starting with that number.
A first improvement would be to avoid resetting your stack s and the running sum after you printed the solution.
Related
I am trying LeetCode problem 1838. Frequency of the Most Frequent Element:
The frequency of an element is the number of times it occurs in an array.
You are given an integer array nums and an integer k. In one operation, you can choose an index of nums and increment the element at that index by 1.
Return the maximum possible frequency of an element after performing at most k operations.
I am getting a Wrong Answer error for a specific test case.
My code
int checkfreq(vector<int>nums,int k,int i)
{
//int sz=nums.size();
int counter=0;
//int i=sz-1;
int el=nums[i];
while(k!=0 && i>0)
{
--i;
while(nums[i]!=el && k>0 && i>=0)
{
++nums[i];
--k;
}
}
counter=count(nums.begin(),nums.end(),el);
return counter;
}
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
vector<int> nums2=nums;
auto distinct=unique(nums2.begin(),nums2.end());
nums2.resize(distance(nums2.begin(),distinct));
int xx=nums.size()-1;
int counter=checkfreq(nums,k,xx);
for(int i=nums2.size()-2;i>=0;--i)
{
--xx;
int temp=checkfreq(nums,k,xx);
if(temp>counter)
counter=temp;
}
return counter;
}
};
Failing test case
Input
nums = [9968,9934,9996,9928,9934,9906,9971,9980,9931,9970,9928,9973,9930,9992,9930,9920,9927,9951,9939,9915,9963,9955,9955,9955,9933,9926,9987,9912,9942,9961,9988,9966,9906,9992,9938,9941,9987,9917,10000,9919,9945,9953,9994,9913,9983,9967,9996,9962,9982,9946,9924,9982,9910,9930,9990,9903,9987,9977,9927,9922,9970,9978,9925,9950,9988,9980,9991,9997,9920,9910,9957,9938,9928,9944,9995,9905,9937,9946,9953,9909,9979,9961,9986,9979,9996,9912,9906,9968,9926,10000,9922,9943,9982,9917,9920,9952,9908,10000,9914,9979,9932,9918,9996,9923,9929,9997,9901,9955,9976,9959,9995,9948,9994,9996,9939,9977,9977,9901,9939,9953,9902,9926,9993,9926,9906,9914,9911,9901,9912,9990,9922,9911,9907,9901,9998,9941,9950,9985,9935,9928,9909,9929,9963,9997,9977,9997,9938,9933,9925,9907,9976,9921,9957,9931,9925,9979,9935,9990,9910,9938,9947,9969,9989,9976,9900,9910,9967,9951,9984,9979,9916,9978,9961,9986,9945,9976,9980,9921,9975,9999,9922]
k = 1524
Output
Expected: 81
My code returns: 79
I tried to solve as many cases as I could. I realise this is a bruteforce approach, but don't understand why my code is giving the wrong answer.
My approach is to convert numbers from last into the specified number. I need to check these as we have to count how many maximum numbers we can convert. Then this is repeated for every number till second last number. This is basically what I was thinking while writing this code.
The reason for the different output is that your xx index is only decreased one unit at each iteration of the i loop. But that loop is iterating for the number of unique elements, while xx is an index in the original vector. When there are many duplicates, that means xx is coming nowhere near the start of the vector and so it misses opportunities there.
You could fix that problem by replacing:
--xx;
...with:
--xx;
while (xx >= 0 && nums[xx] == nums[xx+1]) --xx;
if (xx < 0) break;
That will solve the issue you raise. You can also drop the unique call, and the distinct, nums2 and i variables. The outer loop could just check that xx > 0.
Efficiency is your next problem
Your algorithm is not as efficient as needed, and other tests with huge input data will time out.
Hint 1: checkfreq's inner loop is incrementing nums[i] one unit at a time. Do you see a way to have it increase with a larger amount, so to avoid that inner loop?
Hint 2 (harder): checkfreq is often incrementing the same value in different calls -- even more so when k is large and the section of the vector that can be incremented is large. Can you think of a way to avoid that checkfreq needs to redo that much work in subsequent calls, and can only concentrate on what is different compared to what it had to calculate in the previous call?
CSES problem (https://cses.fi/problemset/task/2216/).
You are given an array that contains each number between 1…n exactly once. Your task is to collect the numbers from 1 to n in increasing order.
On each round, you go through the array from left to right and collect as many numbers as possible. What will be the total number of rounds?
Constraints: 1≤n≤2⋅10^5
This is my code on c++:
int n, res=0;
cin>>n;
int arr[n];
set <int, greater <int>> lastEl;
for(int i=0; i<n; i++) {
cin>>arr[i];
auto it=lastEl.lower_bound(arr[i]);
if(it==lastEl.end()) res++;
else lastEl.erase(*it);
lastEl.insert(arr[i]);
}
cout<<res;
I go through the array once. If the element arr[i] is smaller than all the previous ones, then I "open" a new sequence, and save the element as the last element in this sequence. I store the last elements of already opened sequences in set. If arr[i] is smaller than some of the previous elements, then I take already existing sequence with the largest last element (but less than arr[i]), and replace the last element of this sequence with arr[i].
Alas, it works only on two tests of three given, and for the third one the output is much less than it shoud be. What am I doing wrong?
Let me explain my thought process in detail so that it will be easier for you next time when you face the same type of problem.
First of all, a mistake I often made when faced with this kind of problem is the urge to simulate the process. What do I mean by "simulating the process" mentioned in the problem statement? The problem mentions that a round takes place to maximize the collection of increasing numbers in a certain order. So, you start with 1, find it and see that the next number 2 is not beyond it, i.e., 2 cannot be in the same round as 1 and form an increasing sequence. So, we need another round for 2. Now we find that, 2 and 3 both can be collected in the same round, as we're moving from left to right and taking numbers in an increasing order. But we cannot take 4 because it starts before 2. Finally, for 4 and 5 we need another round. That's makes a total of three rounds.
Now, the problem becomes very easy to solve if you simulate the process in this way. In the first round, you look for numbers that form an increasing sequence starting with 1. You remove these numbers before starting the second round. You continue this way until you've exhausted all the numbers.
But simulating this process will result in a time complexity that won't pass the constraints mentioned in the problem statement. So, we need to figure out another way that gives the same output without simulating the whole process.
Notice that the position of numbers is crucial here. Why do we need another round for 2? Because it comes before 1. We don't need another round for 3 because it comes after 2. Similarly, we need another round for 4 because it comes before 2.
So, when considering each number, we only need to be concerned with the position of the number that comes before it in the order. When considering 2, we look at the position of 1? Does 1 come before or after 2? It it comes after, we don't need another round. But if it comes before, we'll need an extra round. For each number, we look at this condition and increment the round count if necessary. This way, we can figure out the total number of rounds without simulating the whole process.
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char const *argv[])
{
int n;
cin >> n;
vector <int> v(n + 1), pos(n + 1);
for(int i = 1; i <= n; ++i){
cin >> v[i];
pos[v[i]] = i;
}
int total_rounds = 1; // we'll always need at least one round because the input sequence will never be empty
for(int i = 2; i <= n; ++i){
if(pos[i] < pos[i - 1]) total_rounds++;
}
cout << total_rounds << '\n';
return 0;
}
Next time when you're faced with this type of problem, pause for a while and try to control your urge to simulate the process in code. Almost certainly, there will be some clever observation that will allow you to achieve optimal solution.
My objective is to iterate through all combinations of a given amount of 1's and 0's. Say, if I am given the number 5, what would be a sufficiently fast way to list
1110100100,
1011000101, etc.
(Each different combination of 5 1's and 5 0's)
I am attempting to avoid iterating through all possible permutations and checking if 5 1's exist as 2^n is much greater than (n choose n/2). Thanks.
UPDATE
The answer can be calculated efficiently (recurses 10 deep) with:
// call combo() to have calculate(b) called with every valid bitset combo exactly once
combo(int index = 0, int numones = 0) {
static bitset<10> b;
if( index == 10 ) {
calculate(b); // can't have too many zeroes or ones, it so must be 5 zero and 5 one
} else {
if( 10 - numones < 5 ) { // ignore paths with too many zeroes
b[index] = 0;
combo(b, index+1, numones);
}
if( numones < 5 ) { // ignore paths with too many ones
b[index] = 1;
combo(b, index+1, numones++);
}
}
}
(Above code is not tested)
You can transform the problem. If you fix the 1s (or vice versa) then it's simply a matter of where you put the 0s. For 5 1s, there are 5+1 bins, and you want to put 5 elements (0s) in the bins.
This can be solved with a recursion per bin and a loop for each bin (put 0...reaming elements in the bin - except for the last bin, where you have to put all the remaning elements).
Another way to think about it is as a variant of the the string permutation question - just build a vector of length 2n (e.g. 111000) and then use the same algorithm for string permutation to build the result.
Note that the naive algorithm will print duplicate results. However, the algorithm can be easily adapted to ignore such duplicates by keeping a bool array in the recursive function that keeps the values set for the specific bit.
The idea is, given an n number of spaces, empty fields, or what have you, I can place in either a number from 0 to m. So if I have two spaces and just 01 , the outcome would be:
(0 1)
(1 0)
(0 0)
(1 1)
if i had two spaces and three numbers (0 1 2) the outcome would be
(0 1)
(1 1)
(0 2)
(2 0)
(2 2)
(2 1)
and so on until I got all 9 (3^2) possible outcomes.
So i'm trying to write a program that will give me all possible outcomes if I have n spaces and can place in any number from 0 to m in any one of those spaces.
Originally I thought to use for loops but that was quickly shotdown when I realzed I'd have to make one for every number up through n, and that it wouldn't work for cases where n is bigger.
I had the idea to use a random number generator and generate a number from 0 to m but that won't guarantee I'll actually get all the possible outcomes.
I am stuck :(
Ideas?
Any help is much appreciated :)
Basically what you will need is a starting point, ending point, and a way to convert from each state to the next state. For example, a recursive function that is able to add one number to the smallest pace value that you need, and when it is larger than the maximum, to increment the next larger number and set the current one back to zero.
Take this for example:
#include <iostream>
#include <vector>
using namespace std;
// This is just a function to print out a vector.
template<typename T>
inline ostream &operator<< (ostream &os, const vector<T> &v) {
bool first = true;
os << "(";
for (int i = 0; i < v.size (); i++) {
if (first) first = false;
else os << " ";
os << v[i];
}
return os << ")";
}
bool addOne (vector<int> &nums, int pos, int maxNum) {
// If our position has moved off of bounds, so we're done
if (pos < 0)
return false;
// If we have reached the maximum number in one column, we will
// set it back to the base number and increment the next smallest number.
if (nums[pos] == maxNum) {
nums[pos] = 0;
return addOne (nums, pos-1, maxNum);
}
// Otherwise we simply increment this numbers.
else {
nums[pos]++;
return true;
}
}
int main () {
vector<int> nums;
int spaces = 3;
int numbers = 3;
// populate all spaces with 0
nums.resize (spaces, 0);
// Continue looping until the recursive addOne() function returns false (which means we
// have reached the end up all of the numbers)
do {
cout << nums << endl;
} while (addOne (nums, nums.size()-1, numbers));
return 0;
}
Whenever a task requires finding "all of" something, you should first try to do it in these three steps: Can I put them in some kind of order? Can I find the next one given one? Can I find the first?
So if I asked you to give me all the numbers from 1 to 10 inclusive, how would you do it? Well, it's easy because: You know a simple way to put them in order. You can give me the next one given any one of them. You know which is first. So you start with the first, then keep going to the next until you're done.
This same method applies to this problem. You need three algorithms:
An algorithm that orders the outputs such that each output is either greater than or less than every other possible output. (You don't need to code this, just understand it.)
An algorithm to convert any output into the next output and fail if given the last output. (You do need to code this.)
An algorithm to generate the first output, one less (according to the first algorithm) than every other possible output. (You do need to code this.)
Then it's simple:
Generate the first output (using algorithm 3). Output it.
Use the increment algorithm (algorithm 2) to generate the next output. If there is no next output, stop. Otherwise, output it.
Repeat step 2.
Update: Here are some possible algorithms:
Algorithm 1:
Compare the first digits of the two outputs. If one is greater than the other, that output is greater. If they are equal, continue
Repeat step on moving to successive digits until we find a mismatch.
Algorithm 2:
Start with the rightmost digit.
If this digit is not the maximum it can be, increment it and stop.
Are we at the leftmost digit? If so, stop with error.
Move the digit pointer left one digit.
Algorithm 3:
Set all digits to zero.
“i'm trying to write a program that will give me all possible outcomes if I have n spaces and can place in any number from 0 to m in any one of those spaces.”
Assuming an inclusive “to”, let R = m + 1.
Then this is isomorphic to outputting every number in the range 0 through Rn-1 presented in the base R numeral system.
Which means one outer loop to count (for this you can use the C++ ++ increment operator), and an inner loop to extract and present the digits. For the inner loop you can use C++’ / division operator, and depending on what you find most clear, also the % remainder operator. Unless you restrict yourself to the three choices of R directly supported by the C++ standard library, in which case use the standard formatters.
Note that Rn can get large fast.
So don't redirect the output to your printer, and be prepared to wait for a while for the program to complete.
I think you need to look up recursion. http://www.danzig.us/cpp/recursion.html
Basically it is a function that calls itself. This allows you to perform an N number of nested for loops.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I'm trying to efficiently list numbers between 1 and 100. However I have to get rid of numbers with same digits.
Example:
12 according to this rule is the same of 21
13 is 31
14 is 41
so the for loop it won't go over the same numbers.
I'm thinking a few tricks such as getting all the numbers from 1 to 100 and then deleting the found permutations of current number.
The reason I'm asking this because in large limits like 100000 it will fail.
Another example: 124 is equal to 142,241,214,412,421
You can apply recursion. Prototype of this function is then like:
print_digits(int num_of_remaining_digits,int start_from_digit, int current_number);
EDIT: for completion I present here my solution (i think it has better readbility than from Ben Voigt and ascending output order
void print_digits(int num_of_remaining_digits,int start_from_digit, int current_number)
{
if(num_of_remaining_digits == 0)
{
std::cout << current_number << std::endl;
return;
}
for(int i=start_from_digit;i<=9;i++)
{
print_digits(num_of_remaining_digits-1,i,10*current_number+i);
}
}
and here is testing code
http://ideone.com/Xm8Mv
How this works?
It is one of classics in recursion. First there is stopping condition. And then there is main loop.
Main loop where goes from start_from_digit because all generated digits will be in non decreasing order. For instance if current_number is 15 it will call print_digits whith
print_digits(num_of_remaining_digits-1,5,155)
print_digits(num_of_remaining_digits-1,6,156)
print_digits(num_of_remaining_digits-1,7,157)
print_digits(num_of_remaining_digits-1,8,158)
print_digits(num_of_remaining_digits-1,9,159)
In each call it will check if we reached end whit num_of_remaining_digits and if not will continue from digit that is pushed as start_from_digit (2nd param) using current_number
You're look for combination of some characters (0..9) with a certain length (100=2, 1000=3).
Take a look here Algorithm to return all combinations of k elements from n
I would write a class suiting your comparision needs by overloading the correct operators (from the top of my head that should be only less) and go with a std::set.
I would use a hash table, something like this
1) Derive a key from the number derived in such a way that digits with the same number have the same key (e.g. sum the digits, so "124" and "142" have the key 7, or take the product of the digits(+1), so "124" and "142" have the key 30 - have to +1 for the digit 0)
2) Put the numbers in a hash table indexed by its key
Now the test as to whether you already have a number with the same digits is limited to entities in the hash table with the same key. This algorithm requires linear storage and its performance depends on how good a key you can come up with.
#include <stdio.h>
size_t enum_canonical(char* begin, char* end, char min, char max)
{
if (begin == end) {
puts(begin);
putchar('\n');
return 1;
}
size_t result_count = 0;
--end;
for( *end = min; *end <= max; ++*end )
result_count += enum_canonical(begin, end, min, *end);
return result_count;
}
int main(void)
{
char buff[7];
printf("%d results\n", enum_canonical(buff, &(buff[6] = '\0'), '0', '9'));
}
Demo: http://ideone.com/BWGdg
First, observe that your rule excludes multiples of 11. (Why?)
Start by generating all 2-digit numbers with the first digit = 1.
Now, generate all 2-digit numbers with the first digit = 2, but don't generate any numbers that match numbers in the first list.
Repeat for 3, but don't generate any numbers from the first two lists.
Observe that, for any 2-digit number ab, for it to qualify, it must be the case that a < b, or you would have already generated the corresponding number ba.
In PASCAL, just because I'm feeling ornery:
var i:integer; j:integer;
begin
for i := 1 to 8 do
for j := i+1 to 9 do
println(i*10+j);
end;
ADDED A LITTLE LATER
Observe that the numbers you want to generate will always have their digits strictly monotonically increasing. For a number 2abc to qualify, observe that 2 < a < b < c. (Example: 2539 is a match for 2359 and should be rejected.)
Lets take 1 to 1000. Since there are 4 digits in 1000, I print 1 as 0001, so 0001, 0010, 0100, 1000 are same number as per my algorithm. Also 0120, 0012, 0210, 0102, 0201, 0021 are same numbers.
Here is the program:
int main()
{
int i=0, j=0, k=0;
while(i<=9)
{
int unique=(i*100 + j*10 + k);
printf("unique number : %3d\n", unique);
if(j==9 && k==9)
{
i++;
k=i;
j=i;
}
else if(k==9)
{
j++;
k=j;
}
else
k++;
}
}
Seems like it can be as simple as this:
list = {}
for (j = 1 to 100)
if (j is not excluded from list)
list += j;
Really, only the if condition is interesting: needs to examine all relevant properties of the list items.
Create a function which takes a string, and returns an array of strings with all the possible permutations of the characters in that string. It wouldn't be hard, but it would probably be easiest to make recursive. Though, easier said than done.
Once you have that function, and it returns the array, you simply go through the array and remove the indecies which share a common number with one in the array.
I'd use a set for the permutations of the digits of the number:
std::vector<int> list_unwanted = digit_permutations(number);
std::unordered_set<int> set_unwanted(begin(list_unwanted), end(list_unwanted));
Then loop from 0 to the limit, not adding unwanted numbers by checking if they're in the set set_unwanted:
std::vector<int> numbers;
numbers.reserve(limit - set_unwanted.count());
for (int i = 0; i < limit; ++i)
if (!set_unwanted.count(i))
If you have a set of digits, a whatever permutation of this set is not a valid solution, so first of all make a function to estabilish if a set of digits is a permutation of another set.
To get single digits you can divide by 10 recursively, until you get a zero value.
If you put all the digits in an array like [1,2,4], to check if antoher array is a permutation (you check it only if they have the same length) of antoher set:
bool permutation(int *a, int *b, int n) // n leading dimension
{
bool result=true, vector[n]={false};
for(int i=0;i<n;i++)
{
for(int j=0;j<n ;j++)
{
if(a[i]==b[j])
vector[i]=false;
}
}
for(int i=0;i<n && result;i++)
result=(vector[i]==true); // if vector[i] is false, result is false, is
// not a permutation and the loop ends
return result;
}
I haven't tested it, but I think it works, otherwise tell me.
As for putting all digits in an array, I think it's pretty easy.
Once generating all numbers, you check that a certain number is not a permutation of an already taken number.
Here's my idea, for each value put the digits of it in a set. Use that set as a key to another set that keeps track of which numbers have been used. In my case I use a bit field as a set for the digits, i.e. digit 0 is represented by a 1, digit 1 is represented by a 2 (2 by a 4 and so on). Too tired to explain, here's tha codez:
unsigned int get_digits(int v)
{
unsigned int rv = 0;
do
{
rv |= 1 << (v % 10);
v /= 10;
} while(v);
return rv;
}
void unique_ints(int n)
{
std::set<unsigned int> used_combinations;
for(int i = 0; i < n; ++i)
{
const unsigned int d = get_digits(i);
if(used_combinations.find(d) == used_combinations.end())
{
used_combinations.insert(d);
// cout or some other way to store the value
std::cout << i << std::endl;
}
}
}