Optimization or New Algorithm to solve this? [closed] - c++

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am trying to solve this problem :
Little girl has an array of n elements (the elements of the array are indexed starting from 1).
Also, there are "q" queries, each one is defined by a pair of integers li, ri (1 ≤ li ≤ ri ≤ n). You need to find for each query the sum of elements of the array with indexes from li to ri, inclusive.
The little girl found the problem rather boring. She decided to reorder the array elements before replying to the queries in a way that makes the sum of query replies maximum possible. Your task is to find the value of this maximum sum.
Input:
The first line contains two space-separated integers n (1 ≤ n ≤ 10^5) and q (1 ≤ q ≤ 10^5) — the number of elements in the array and the number of queries, correspondingly.
The next line contains n space-separated integers ai (1 ≤ ai ≤ 10^5) — the array elements.
Each of the following q lines contains two space-separated integers li and ri (1 ≤ li ≤ ri ≤ n) — the i-th query.
Output:
In a single line print a single integer — the maximum sum of query replies after the array elements are reordered.
Sample testcases:
input:
3 3
5 3 2
1 2
2 3
1 3
output
25
input
5 3
5 2 4 1 3
1 5
2 3
2 3
output
33
I have knowledge of Segment tree so i applied Lazy propagation approach through segment tree.
My Effort code :
#include <iostream>
#include <string>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
#include <list>
#include <cmath>
#include <stack>
using namespace std;
#define scan(x) scanf("%d",&x)
#define print(x) printf("%d ",x)
#define println(x) printf("%d\n",x)
#define For(i,a,j,k) for (i = a; i < j; i+= k)
#define For_back(i,a,j,k) for (i = j; i >= a; i-= k)
#define SET(a,x) memset(a,x,sizeof(a))
#define mod 1000000007
#define inf 0x7fffffff
#define Max 2000000
typedef pair<int,int> pii;
typedef pair<pii,int> piii;
long long int tree[3*Max];
long long int lazy[3*Max];
void update(int node,int a,int b,int i,int j,long long int value)
{
if (lazy[node]!= 0)
{
tree[node] += lazy[node];
if (a != b)
{
lazy[2*node] += lazy[node];
lazy[2*node+1] += lazy[node];
}
lazy[node] = 0;
}
if (a > b || a > j || b < i)
return;
if (a >= i && b <= j)
{
tree[node] += value;
if (a != b)
{
lazy[2*node] += value;
lazy[2*node+1] += value;
}
return;
}
int mid = (a+b)/2;
update(2*node,a,mid,i,j,value);
update(2*node+1,mid+1,b,i,j,value);
tree[node] = (tree[2*node]+tree[2*node+1]);
}
long long int query(int node,int a,int b,int i,int j)
{
if (a> b || a > j || b < i) return 0;
if (lazy[node] != 0)
{
tree[node] += lazy[node];
if (a != b)
{
lazy[2*node] += lazy[node];
lazy[2*node+1] += lazy[node];
}
lazy[node] = 0;
}
if (a >= i && b <= j)
return tree[node];
int mid = (a+b)/2;
long long int q1 = query(2*node,a,mid,i,j);
long long int q2 = query(2*node+1,mid+1,b,i,j);
return ((q1+q2));
}
int main()
{
SET(lazy,0);
SET(tree,0);
int n,m;
cin >> n >> m;
int i,j;
int arr[n];
For(i,0,n,1)
{
cin >> arr[i];
}
sort(arr,arr+n);
For(i,0,m,1)
{
long long int num1,num2;
cin >> num1 >> num2;
update(1,0,n-1,num1-1,num2-1,1);
}
long long int my[n];
For(i,0,n,1)
{
long long int number = query(1,0,n-1,i,i);
my[i] = number;
}
sort(my,my+n);
long long int sum = 0;
For_back(i,0,n-1,1){
sum += my[i]*arr[i];
}
cout << sum << endl;
return 0;
}
My approach for this was simple just to do as said using segment tree and lastly print the answer.
My question is is there any simpler Algo for this ? or should i optimize my segment tree code ?

Concept:
"You have to fix the biggest element from array to the index that is queried most times and then second biggest element to second most queried element
Here's implementation of my method:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
int main()
{
LL n,q,l,r,i;
cin>>n>>q;
LL arr[n];
for(i=0;i<n;i++)
cin>>arr[i];
LL freq[n];
memset(freq,0,sizeof freq);
sort(arr,arr+n);
for(i=0;i<q;i++)
{
cin>>l>>r;
freq[l-1]++; // DP method of freq
if(r<n)
freq[r]--;
}
for(i=1;i<n;i++)
freq[i]+=freq[i-1];
sort(freq,freq+n);
LL ans=0;
for(i=n-1;i>=0;i--)
if(freq[i])
ans+=arr[i]*freq[i];
cout<<ans<<endl;
return 0;
}
Yeah you have to sort the array and then sort frequency and then multiply number with frequency and that will result in maximum sum..
Method to keep count:
Don't update each time from li to ri.
instead just increase the count at each starting position and at one more than end position because you have to include till end.
lastly sum all count. in O(n). and you can know how many time each was increased. sort it and sort the given array and multiply number with frequency so obtained and you have you answer.
input: 5 3
array : 5 2 4 1 3
1st query: 1 5
freq update = 1 0 0 0 0
2nd query: 2 3
freq update =1 1 0 -1 0
3rd query: 2 3
freq update= 1 2 0 -2 0
collective freq=1 3 3 1 1
sorted freq= 1 1 1 3 3
sorted array =1 2 3 4 5
ans =33

I think this will work - comments invited
Create an array of dimension n called count and initialize it to 0
Go through the Q array
For each query
- From li to ri increment count by 1 i.e. the count of elements li to ri
Sort the array n
Sort the count array (remember the index)
Pick up the highest of the count and at the corresponding index put the highest element from N
Continue this for all elements
Basically we are ensuring that the highest element occurs the highest number of times (when referred by the query)

This is what I'd do:
Created a (hash)map index->count. Go through all queries and for each index in the range, increment the count (*).
Order the elements in the array by size, decending (called values now).
Extract the counts from your hashmap (the indexes are irrelevant now, because we don't need them again and the array is reordered accordingly) and also order them decending.
Iterate through the ordered array of counts and sum up sum += counts[i] * values[i]
let's say your array is
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
queries are:
q1: 1-3
q2: 2-4
q3: 3-5
the map:
1->1
2->2
3->3
4->2
5->1
the counts sorted:
3,2,2,1
one of the perfect reorderings (irrelevant for the algorithm, because only the sum is required)
6,7,9,8,5,4,3,2,1,0
sum for queries:
(6 + 7 + 9) + (7 + 9 + 8) + (9 + 8 + 5) = 68
with the algorithm:
3 * 9 + 2 * 8 + 2 * 7 + 1 * 6 + 1 * 5 = 68
(*) If you want to speed this up, you can use an array / vector of size n instead of a map and use indexes as keys. If just mentioned the map in my example because it makes the idea more evident

Related

O(n^2) algorithm to find largest 3 integer arithmetic series

The problem is fairly simple. Given an input of N (3 <= N <= 3000) integers, find the largest sum of a 3-integer arithmetic series in the sequence. Eg. (15, 8, 1) is a larger arithmetic series than (12, 7, 2) because 15 + 8 + 1 > 12 + 7 + 2. The integers apart of the largest arithmetic series do NOT have to be adjacent, and the order they appear in is irrelevant.
An example input would be:
6
1 6 11 2 7 12
where the first number is N (in this case, 6) and the second line is the sequence N integers long.
And the output would be the largest sum of any 3-integer arithmetic series. Like so:
21
because 2, 7 and 12 has the largest sum of any 3-integer arithmetic series in the sequence, and 2 + 7 + 12 = 21. It is also guaranteed that a 3-integer arithmetic series exists in the sequence.
EDIT: The numbers that make up the sum (output) have to be an arithmetic series (constant difference) that is 3 integers long. In the case of the sample input, (1 6 11) is a possible arithmetic series, but it is smaller than (2 7 12) because 2 + 7 + 12 > 1 + 6 + 11. Thus 21 would be outputted because it is larger.
Here is my attempt at solving this question in C++:
#include <bits/stdc++.h>
using namespace std;
vector<int> results;
vector<int> middle;
vector<int> diff;
int main(){
int n;
cin >> n;
int sizes[n];
for (int i = 0; i < n; i++){
int size;
cin >> size;
sizes[i] = size;
}
sort(sizes, sizes + n, greater<int>());
for (int i = 0; i < n; i++){
for (int j = i+1; j < n; j++){
int difference = sizes[i] - sizes[j];
diff.insert(diff.end(), difference);
middle.insert(middle.end(), sizes[j]);
}
}
for (size_t i = 0; i < middle.size(); i++){
int difference = middle[i] - diff[i];
for (int j = 0; j < n; j++){
if (sizes[j] == difference) results.insert(results.end(), middle[i]);
}
}
int max = 0;
for (size_t i = 0; i < results.size(); i++) {
if (results[i] > max) max = results[i];
}
int answer = max * 3;
cout << answer;
return 0;
}
My approach was to record what the middle number and the difference was using separate vectors, then loop through the vectors and search if the middle number minus the difference is in the array, where it gets added to another vector. Then the largest middle number is found and multiplied by 3 to get the sum. This approach made my algorithm go from O(n^3) to roughly O(n^2). However, the algorithm doesn't always produce the correct output (and I can't think of a test case where this doesn't work) every time, and since I'm using separate vectors, I get a std::bad_alloc error for large N values because I am probably using too much memory. The time limit in this question is 1.4 sec per test case, and memory limit is 64 MB.
Since N can only be max 3000, O(n^2) is sufficient. So what is an optimal O(n^2) solution (or better) to this problem?
So, a simple solution for this problem is to put all elements into an std::map to count their frequencies, then iterate over the first and second element in the arithmetic progression, then search the map for the third.
Iterating takes O(n^2) and map lookups and find() generally takes O(logn).
include <iostream>
#include <map>
using namespace std;
const int maxn = 3000;
int a[maxn+1];
map<int, int> freq;
int main()
{
int n; cin >> n;
for (int i = 1; i <= n; i++) {cin >> a[i]; freq[a[i]]++;} // inserting frequencies
int maxi = INT_MIN;
for (int i = 1; i <= n-1; i++)
{
for (int j = i+1; j <= n; j++)
{
int first = a[i], sec = a[j]; if (first > sec) {swap(first, sec);} //ensure that first is smaller than sec
int gap = sec - first; //calculating difference
if (gap == 0 && freq[first] >= 3) {maxi = max(maxi, first*3); } //if first = sec then calculate immidiately
else
{
int third1 = first - gap; //else there're two options for the third element
if (freq.find(third1) != freq.end() && gap != 0) {maxi = max(maxi, first + sec + third1); } //finding third element
}
}
}
cout << maxi;
}
Output : 21
Another test :
6
3 4 5 7 7 7
Output : 21
Another test :
5
10 10 9 8 7
Output : 27
You can try std::unordered_map to try and reduce the complexity even more.
Also see Why is "using namespace std;" considered bad practice?
The sum of a 3-element arithmetic progression is 3-times the middle element, so I would search around a middle element, and would start the search from the "upper" end of the "array" (and have it sorted). This way the first hit is the largest one. Also, the actual array would be a frequency-map, so elements are unique, but still track if any element has 3 copies, because that can become a hit (progression by 0).
I think it may be better to create the frequency-map first, and sort it later, simply because it may result in sorting fewer elements - though they are going to be pairs of value and count in this case.
function max3(arr){
let stats=new Map();
for(let value of arr)
stats.set(value,(stats.get(value) || 0)+1);
let array=Array.from(stats); // array of [value,count] arrays
array.sort((x,y)=>y[0]-x[0]); // sort by value, descending
for(let i=0;i<array.length;i++){
let [value,count]=array[i];
if(count>=3)
return 3*value;
for(let j=0;j<i;j++)
if(stats.has(2*value-array[j][0]))
return 3*value;
}
}
console.log(max3([1,6,11,2,7,12])); // original example
console.log(max3([3,4,5,7,7,7])); // an example of 3 identical elements
console.log(max3([10,10,9,8,7])); // an example from another answer
console.log(max3([1,2,11,6,7,12])); // example with non-adjacent elements
console.log(max3([3,7,1,1,1])); // check for finding lowest possible triplet too

Whats the difference between this 2 flowing code with same objective

Question:
Starting with a 1-indexed array of size n filled with all zeroes, you are required to perform the following operation m times:
Each operation contains 3 integers a, b and k. You are required to add k to the value at all the indices from a to b (both inclusive).
Once all operations have been performed, return the maximum value in the array.
Input Format:
The first line contains two space-separated integers n and m, the size of the array and the number of operations respectively.
Each of the next m lines contains three space-separated integers a, b and k, the left index, right index and integer to add respectively.
Constraints:
1 <= n, m <= 10^5
1 <= a <= b <= n
-10^9 <= k <= 10^9
Examples:
Sample Input 1:
10 3
1 5 3
4 8 7
6 9 1
Sample Output 1:
10
Explanation:
Given n = 10 and m = 3
Queries are interpreted as follows:
a b k
1 5 3
4 8 7
6 9 1
Add the values of k between the indices a and b inclusive:
1 2 3 4 5 6 7 8 9 10 (index)
[0,0,0,0,0,0,0,0,0,0] (Initially)
[3,3,3,3,3,0,0,0,0,0] (After Query 1)
[3,3,3,10,10,7,7,7,0,0] (After Query 2)
[3,3,3,10,10,8,8,8,1,0] (After Query 3)
The largest value is 10 after all operations are performed.
Sample Input 2:
5 3
1 2 100
2 5 100
3 4 100
Sample Output:
200
Explanation:
After the first update the list is 100 100 0 0 0.
After the second update list is 100 200 100 100 100.
After the third update list is 100 200 200 200 100.
The maximum value is 200.
This code ran and gave the correct output:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<long long int> arr(n, 0);
for (int i = 0; i < m; i++) {
int start, finish, value;
cin >> start >> finish >> value;
arr[start - 1] += value;
arr[finish] -= value;
}
long long int ans = 0;
long long int current = 0;
for (int value : arr) {
current += value;
if (current > ans) {
ans = current;
}
}
cout << ans;
return 0;
}
but this code is giving me segment fault:
#include <bits/stdc++.h>
using namespace std;
int main(){
long long int n,m;
cin>>n>>m;
long long int x[n] ={0};
for(int i=0;i<m;i++)
{
long long int a,b,k;
cin>>a>>b>>k;
x[a-1] += k;
if(b<n)
x[b] -= k;
}
long long int max=0,current=0;
for(int i=0;i<n;i++)
{
current=x[i]+current;
if(max<current)
max=current;
}
cout<<max<< endl;
return 0;
}
Well the problem in your code is in the line x[b] -= k;
Since b is 1-indexed, this means that b can be upto n but your array of size n has indices from 0 to n-1 and if there is a query where b = n, then you get segmentation fault.
You can correct this by placing an if condition just before this line like this:
if (b < n)
Also, in the first code, you should have encountered the same problem but somehow your compiler overlooked the indexing problem in vector.

Find the length of longest consecutive sub-sequence of a sequence in O(n) time where all elements are less than 10^6

I have to find the the length of largest increasing sub-sequence of an array such that difference between any two consecutive elements of sub-sequence is 1
For example: {5,4,2,1,6,2,3,4,5}
length of largest consecutive increasing sub-sequence : 5 {1,2,3,4,5}
SO far I have tried this:
#include <iostream>
using namespace std;
int a[1000001];
int m[1000001]={0};
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
m[a[i]]=i;
}
int maxm=0;
for(int i=1;i<=n;i++)
{
if(m[a[i]-1]==0 || m[a[i]]<=m[a[i]-1])
{
int k=a[i];
int prev = m[k];
k++;
int c=1;
while(m[k]>prev)
{
c++;
prev=m[k];
k++;
}
maxm=max(maxm,c);
}
}
cout<<maxm;
return 0;
}
But this is giving wrong answer for cases like{2,2,1,2,3,1,2,3,4,3,5}
Any help would be appreciated.
Let's discuss the algorithm here rather than jumping to the answer/code.
Associate a value with each element. The value with any element X will be how many elements from X-1 till 1 have I seen before I encountered X and add 1 to the value because now we have encountered X also.
So since an element of an array is strictly between 1 <= A[i] <= 106 we are in luck.
We make an array for each of the elements, whether they appear in the array or not. This kind of approach is similar to Hash Table
but since all our elements are integers, we are using an array as a simple hash table where key is the index of the array and value is the hash_table[index] i.e.. the value stores in the index.
Now lets dry run our approach for one of our sample inputs :
5 1 5 6 2 3 8 7 4
Initiall the hash-table looks like this :
hash_table = {0,0,0,0,0,0,0,0,0}; // Not showing indices > 8 because they won't be affected.
Now we encounter 5 :
We look up the value of hash_table[4] and add 1 and put it as the value of 5 i.e. hash_table[5] = hash_table[4] + 1
So hash table looks like this now :
hash_table = {0,0,0,0,0,1,0,0,0};
Then we encounter 1 : we do the same thing :
hash_table = {0,1,0,0,0,1,0,0,0};
Like that after taking in all the numbers hash_table looks like this :
hash_table = {0,1,2,3,4,1,2,3,1}
Our answer is the maximum value of the hash_table, which is 4.
Talk is cheap show me the code :
#include <stdio.h>
#define MAX (int)1e6
int h[MAX];
int main ()
{
int N,i,max=0,temp;
scanf ("%d",&N);
for (i=0;i<N;i++)
{
scanf ("%d",&temp);
h[temp] = h[temp - 1] + 1;
if (h[temp] > max)
max = h[temp];
}
printf ("%d\n",max);
return 0;
}
So what if you can't upvote. You can still accept this answer if you found it useful !
You are thinking a bit too complicated. You just have to iterate through the array once and count the lenght of sequences and remeber the longest one :
int main() {
int size;
int input[100000];
/* ... get your input with size elements ... */
int current = 1;
int biggest = 1;
for (int i=1;i<size;i++) {
if (input[i] == input[i-1] + 1) { current++; }
else {
if (current > biggest) { biggest = current; }
current = 1;
}
}
}

Sequence of n numbers - compute all possible k-subsequence of "lucky" numbers

I have a problem with one task, so if you could help me a little bit.
Numbers are "lucky" or "unlucky". Number is "lucky" just if every
digit 7
or every digit is 4. So "lucky" numbers are for example 4, 44, 7, 77.
"Unlucky" are the others numbers.
You will get sequence of n-elements and number K. Your task is to
compute number of all possible k-elements subsequence, which fulfill a one
condition. The condition is that in the subsequence mustn't be two same "lucky"
numbers. So for example there mustn't 77 and 77...
Output number of all possible k-elements subsequence mod 10^9+7
0 < N,K < 10^5
Few examples:
Input:
5 2
7 7 3 7 77
Output:
7
Input:
5 3
3 7 77 7 77
Output:
4
Input:
34 17
14 14 14 ... 14 14 14
Output:
333606206
I have code which seems to work, but it is too slow when I try to compute binomial coefficient. I'm using map. In string I store number in string format. In second - int - part of the map is number which represents how many times was that number(in the first map parameter) used. So now I have stored every "unlucky" numbers stored together. Also every same "lucky" number is together. When I have it stored like this, I just compute all multiplications. For example:
Input
5 2
3 7 7 77 7
Are stored like this: map["other"] = 1 map["7"] = 3 map["77"] = 1
Because k = 2 --> result is: 1*3 + 1*1 + 1*3 = 7.
I think problem is with computing binomial coefficient. For the third example it needs to compute (34 choose 17) and it is computing very long time.I've found this article and also this , but I don't understand how they are solving this problem.
My code:
#include<iostream>
#include<string>
#include<map>
#include <algorithm>
#include <vector>
using namespace std;
int binomialCoeff(int n, int k)
{
// Base Cases
if (k == 0 || k == n)
return 1;
// Recur
return binomialCoeff(n - 1, k - 1) + binomialCoeff(n - 1, k);
}
int main()
{
int n, k;
cin >> n >> k;
map<string, int> mapa; // create map, string is a number, int represents number of used string-stored numbers ---> so if 7 was used two times, in the map it will be stored like this mapa["7"] == 2 and so on)
for (int i = 0; i < n; i++) // I will load number as string, if this number is "lucky" - digist are all 7 or all 4
{ // every "unlucky" numbers are together, as well as all same "lucky" numbers ---> so 77 and 77 will be stored in one element....
string number;
cin >> number;
char digit = number[0];
bool lucky = false;
if (digit == '7' || digit == '4')
lucky = true;
for (int j = 1; j < number.length(); j++) {
if (digit != '7' && digit != '4')
break;
if (number[j] != digit) {
lucky = false;
break;
}
}
if (lucky)
mapa[number]++;
else
mapa["other"]++;
}
vector<bool> v(mapa.size());
bool lack = k > mapa.size(); //lack of elements in map --> it is when mapa.size() < k; i. e. number of elements in array can't make k-element subsequence.
int rest = lack ? k - mapa.size() + 1 : 1; // how many elements from "unlucky" numbers I must choose, so it makes base for binomial coefficient (n choose rest)
if (lack) //if lack is true, different size of vector
fill(v.begin() + mapa.size(), v.end(), true);
else
fill(v.begin() + k, v.end(), true);
int *array = new int[mapa.size()]; //easier to manipulate with array for me
int sum = 0;
int product = 1;
int index = 0;
for (map<string, int> ::iterator pos = mapa.begin(); pos != mapa.end(); ++pos) // create array from map
{
if (lack && pos->first == "other") { //if lack of elements in map, the number in elemets representing "unlucky" numbers will be binomial coefficient (mapa["other] choose rest)
array[index++] = binomialCoeff(mapa["other"], rest);
continue;
}
array[index++] = pos->second;
}
do { // this will create every posible multiplication for k-elements subsequences
product = 1;
for (int i = 0; i < mapa.size(); ++i) {
if (!v[i]) {
product *= array[i];
}
}
sum += product;
} while (next_permutation(v.begin(), v.end()));
if (mapa["other"] >= k && mapa.size() > 1) { // if number of "unlucky" numbers is bigger than k, we need to compute all possible k-elements subsequences just from "unlucky" number, so binomial coefficient (mapa["other] choose k)
sum += binomialCoeff(mapa["other"], k);
}
cout << sum % 1000000007 << endl;
}

Sum of values at common indexes in all subsets?

In a recent problem where i have to sum all values at common indexes in all possible subsets of size k in array of size n.
For eg: If
array ={1,2,3}
Its subsets (k=2) will be (x [i] , x [j]) where i < j
1 2
1 3
2 3
Sum:4,8
Firstly I have used recursion (same that of generating all subsets)
int sum_index[k]={0};
void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
if(q==k)
{
for(int i=0;i<k;i++)
sum_index[i]+=temp[i];
}
else
{
for(int i=r;i<n;i++)
{
temp[q]=array[i];
sub_set(value,n,k,temp,q+1,i+1);
}
}
}
Problem is its taking too much time then expected .
Then i modified it to...
void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
if(q==k)
{
return;
}
else
{
for(int i=r;i<n;i++)
{
temp[q]=array[i];
sum_index[q]+=temp[q]; //or sum_index[q]+=s[i];
sub_set(value,n,k,temp,q+1,i+1);
}
}
}
Still taking too much time!!
Is there any other approach to this problem?? Or any other modification i needed that i am unaware of??
Instead of iterating through the possible sub-sets, think of it a combinatorics problem.
To use your example of k=2 and {1,2,3}, let's just look at the first value of the result. It has two 1's and one 2. The two 1's correspond to the number one element sets that can be made from {2, 3} and the one 2 corresponds to the number of one element sets that can be made from {3}. A similar arrangement exists for the one 2 and two 3's in the second element of the result and looking at the subsets of the elements that appear before the element being considered.
Things get a bit more complicated when k>2 because then you will have to look for the number of combinations of elements before and after the element being considered, but the basic premise still works. Multiply the number of possible subsets before times the number of subsets afterwards and that will tell you how many times each element contributes to the result.
A solution in O(n^2) instead of O(n!):
First a tiny (:)) bit of explanation, then some code:
I´m going to assume here that your array is sorted (if not, use std::sort first). Additionally, I´m going to work with the array values 1,2,3,4... here, if you array consists arbitrary values (like 2 8 17), you´ll have to think of it as the indices (ie. 1=>2, 2=>8 etc.)
Definition: (x choose y) means the binomial coefficient, how it is calculated is in the link too. If you have an array size a and some k for the subset size, (a choose k) is the number of permutations, eg. 3 for your example: (1,2), (1,3) and (2,3).
You want the sum for each column if you write the permutations under each other, this would be easy if you knew for each column how many times each array element occurs, ie. how many 1´s, 2´s and 3´s for the first, and how many for second column (with k=2).
Here a bigger example to explain: (1,2,3,4,5) and all possible k´s (each in one block):
1
2
3
4
5
12
13
14
15
23
24
25
34
35
45
123
124
125
134
135
145
234
235
245
345
... (didn´t write k=4)
12345
Let´s introduce column indices, 0<=c<k, ie. c=0 means the first column, c=1 the second and so on; and the array size s=5.
So, looking eg. at the k=3-block, you´ll notice that the lines beginning with 1 (column c=0) have all permutations of the values (2,3,4,5) for k=2, more generally a value x in column c has all permutations for values x+1 to s after it. The values from from x+1 to s are s-x different values, and after column c there are k-c-1 more columns. So, for a value x, you can calculate ((s-x) choose (k-c-1)).
Additionally, the first column has only the values 1,2,3, the last two numbers are not here because after this column there are two more columns.
If you do this for the first column, it works well. Eg. with value 1 in the first column of k=3 above:
count(x) = ((s-x) choose (k-c-1)) = (4 choose 2) = 6
and indeed there are six 1 there. Calculate this count for every array value, multiply x*count(x), and sum it up for every x, that´s the result for the first column.
The other columns are a tiny bit harder, because there can be multiple "permutation blocks" of the same number. To start with, the step above needs a small adjustment: You need a muliplier array somewhere, one multiplier for each array value, and in the beginning each multiplier is 1. In the calculation x*count(x) above, take x*count(x)*muliplier(x) instead.
In the k=3-example, 1 in the first column can be followed by 2,3,4, 2 can be followed by 3,4, and 3 by 4. So the 3-based permutations of the second column need to be counted twice, and the 4-based even three times; more generally so many times like there are smaller values in the previos colums. Multiply that to the current multiplier.
...
Some code:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// factorial (x!)
unsigned long long fact(unsigned char x)
{
unsigned long long res = 1;
while(x)
{
res *= x;
x--;
}
return res;
}
//binomial coefficient (n choose k)
unsigned long long binom(unsigned char n, unsigned char k)
{
if(!n || !k) return 1;
return (fact(n) / fact(k)) / fact(n-k);
}
//just for convenience
template<class T> void printvector(std::vector<T> data)
{
for(auto l : data) cout << l << " ";
cout << endl;
}
std::vector<unsigned long long> calculate(std::vector<int> data, int k)
{
std::vector<unsigned long long> res(k, 0); //result data
std::vector<unsigned long long> multiplier(data.size(), 1);
if(k < 1 || k > 255 || data.size() < 1) return res; //invalid stuff
std::sort(data.begin(), data.end()); //as described
for(int column = 0; column < k; column++) //each column separately
{
//count what to multiply to the multiplier array later
std::vector<unsigned long long> newmultiplier(data.size(), 0);
//for each array element in this column
for(int x = column; x <= (data.size() + column - k); x++)
{
//core calculation
res[column] += data[x] * multiplier[x] * binom(data.size() - x - 1, k - column - 1);
//counting the new multiplier factor
for(int helper = x + 1; helper < data.size(); helper++)
newmultiplier[helper]++;
}
//calculating new multiplier
for(int x = 0; x < data.size(); x++)
{
if(newmultiplier[x])
multiplier[x] *= newmultiplier[x];
}
}
return res;
}
int main() {
printvector(calculate({1,2,3}, 2)); //output 4 8
return 0;
}
std::next_permutation may help:
std::vector<int> sub_set(const std::vector<int>& a, int k)
{
std::vector<int> res(k, 0);
std::vector<bool> p(a.size() - k, false);
p.resize(a.size(), true);
do
{
int index = 0;
for (std::size_t i = 0; i != p.size(); ++i) {
if (p[i]) {
res[index++] += a[i];
}
}
} while (std::next_permutation(p.begin(), p.end()));
return res;
}
Live Demo