Given integers N and K representing the number of batches and number of students in each batch respectively, and a 2D array ratings[][] of size N * K where each row has ratings for every K students and Q queries of type {a, b}. The task for each query is to count the number of groups of N students possible by selecting a student from each batch, such that sum of ratings in each group lies in the range [a, b] inclusively.
Examples:
Input: N = 2, K = 3, ratings[][]= { {1, 2, 3}, {4, 5, 6} }, Q = 2, Queries[][]={ {6, 6}, {1, 6} }
Output: 2 3
Explanation:
All possible groups of size N(=2) are:
1 + 4 = 5
1 + 5 = 6
1 + 6 = 7
2 + 4 = 6
2 + 5 = 7
2 + 6 = 8
3 + 4 = 7
3 + 5 = 8
3 + 6 = 9
Query 1: The groups whose sum in range of (6, 6) inclusive are (1 + 5), (2 + 4) is 2.
Query 2: The groups whose sum in range of (1, 6) inclusive are (1 + 4), (1 + 5), (2 + 4) is 3.
Input: N = 3, K = 3, ratings[][]={ {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }, Q = 2, Queries[][]={ {10, 13}, {1, 7} }
Output: 4 0
Explanation:
Out of All possible groups of size N(=3):
Query 1: The groups whose sum in range (10, 13) inclusive is (1 + 4 + 7), (1 + 5 + 7), (2 + 4 + 7), (1 + 4 + 8) is 4.
Query 2: The groups whose sum in range of (1, 7) inclusive is 0.
Naive Approach: The simplest approach is to use recursion to generate all possible groups of size N. At each step of recursion calculate the sum that lies within the range of a query and find the number of groups that lie in the given range.
Time Complexity: O(NK)
Auxiliary Space: O(1)
Efficient Approach: The above approach can be optimized by using Dynamic Programming. The idea is that if the number of times the sum S appears in the ith row is known, then the Prefix Sum Technique can be used to answer all the queries in constant time. So in this way, the Overlapping Subproblems are calculated only once reducing the exponential time into polynomial time. Below are the steps:
- Initialize auxiliary array dp[][] where dp[i][sum] is the number of times a sum is present in the ith row.
- For each batch, i iterate through all possible sum S, and for each j students, if sum S is greater than the current rating ratings[i][j] then update the current dp state dp[i][S] as:
dp[i][S] = dp[i][S] + dp[i – 1][sum – rating[i][j]]
- After the above steps, dp[N – 1][sum], which is the number of times the sum appears in the (N – 1)th row.
- To answer each queries efficiently find the prefix sum of the last row i.e., dp[N – 1][S] for all values of sum S.
- Now, the number of ways to form groups in the given range [a, b] is dp[N – 1][b] – dp[N – 1][a – 1].
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
#define n 2
#define k 3
void numWays( int ratings[n][k], int queries[][2])
{
int dp[n][10000 + 2];
for ( int i = 0; i < k; i++)
dp[0][ratings[0][i]] += 1;
for ( int i = 1; i < n; i++) {
for ( int sum = 0; sum <= 10000; sum++)
{
for ( int j = 0; j < k; j++)
{
if (sum >= ratings[i][j])
dp[i][sum]
+= dp[i - 1][sum - ratings[i][j]];
}
}
}
for ( int sum = 1; sum <= 10000; sum++)
{
dp[n - 1][sum] += dp[n - 1][sum - 1];
}
for ( int q = 0; q < 2; q++)
{
int a = queries[q][0];
int b = queries[q][1];
cout << dp[n - 1][b] - dp[n - 1][a - 1] << " " ;
}
}
int main()
{
int ratings[n][k] = { { 1, 2, 3 }, { 4, 5, 6 } };
int queries[][2] = { { 6, 6 }, { 1, 6 } };
numWays(ratings, queries);
return 0;
}
|
Java
import java.util.*;
public class Main {
public static void
numWays( int [][] ratings,
int queries[][],
int n, int k)
{
int dp[][] = new int [n][ 10000 + 2 ];
for ( int i = 0 ; i < k; i++)
dp[ 0 ][ratings[ 0 ][i]] += 1 ;
for ( int i = 1 ; i < n; i++)
{
for ( int sum = 0 ; sum <= 10000 ; sum++)
{
for ( int j = 0 ; j < k; j++)
{
if (sum >= ratings[i][j])
dp[i][sum]
+= dp[i - 1 ]
[sum - ratings[i][j]];
}
}
}
for ( int sum = 1 ; sum <= 10000 ; sum++) {
dp[n - 1 ][sum] += dp[n - 1 ][sum - 1 ];
}
for ( int q = 0 ; q < queries.length; q++) {
int a = queries[q][ 0 ];
int b = queries[q][ 1 ];
System.out.print(dp[n - 1 ][b] - dp[n - 1 ][a - 1 ]
+ " " );
}
}
public static void main(String args[])
{
int N = 2 , K = 3 ;
int ratings[][] = { { 1 , 2 , 3 }, { 4 , 5 , 6 } };
int queries[][] = { { 6 , 6 }, { 1 , 6 } };
numWays(ratings, queries, N, K);
}
}
|
Python3
def numWays(ratings, queries,
n, k):
dp = [[ 0 for i in range ( 10002 )]
for j in range (n)];
for i in range (k):
dp[ 0 ][ratings[ 0 ][i]] + = 1 ;
for i in range ( 1 , n):
for sum in range ( 10001 ):
for j in range (k):
if ( sum > = ratings[i][j]):
dp[i][ sum ] + = dp[i - 1 ][ sum -
ratings[i][j]];
for sum in range ( 1 , 10001 ):
dp[n - 1 ][ sum ] + = dp[n - 1 ][ sum - 1 ];
for q in range ( len (queries)):
a = queries[q][ 0 ];
b = queries[q][ 1 ];
print (dp[n - 1 ][b] -
dp[n - 1 ][a - 1 ],
end = " " );
if __name__ = = '__main__' :
N = 2 ;
K = 3 ;
ratings = [[ 1 , 2 , 3 ],
[ 4 , 5 , 6 ]];
queries = [[ 6 , 6 ],
[ 1 , 6 ]];
numWays(ratings, queries, N, K);
|
C#
using System;
class GFG{
public static void numWays( int [,] ratings,
int [,]queries,
int n, int k)
{
int [,]dp = new int [n, 10000 + 2];
for ( int i = 0; i < k; i++)
dp[0, ratings[0, i]] += 1;
for ( int i = 1; i < n; i++)
{
for ( int sum = 0; sum <= 10000; sum++)
{
for ( int j = 0; j < k; j++)
{
if (sum >= ratings[i, j])
dp[i, sum] += dp[i - 1,
sum - ratings[i, j]];
}
}
}
for ( int sum = 1; sum <= 10000; sum++)
{
dp[n - 1, sum] += dp[n - 1, sum - 1];
}
for ( int q = 0; q < queries.GetLength(0); q++)
{
int a = queries[q, 0];
int b = queries[q, 1];
Console.Write(dp[n - 1, b] -
dp[n - 1, a - 1] + " " );
}
}
public static void Main(String []args)
{
int N = 2, K = 3;
int [,]ratings = { { 1, 2, 3 }, { 4, 5, 6 } };
int [,]queries = { { 6, 6 }, { 1, 6 } };
numWays(ratings, queries, N, K);
}
}
|
Javascript
<script>
var n = 2;
var k = 3;
function numWays(ratings, queries)
{
var dp = Array.from(
Array(n), ()=>Array(10002).fill(0));
for ( var i = 0; i < k; i++)
dp[0][ratings[0][i]] += 1;
for ( var i = 1; i < n; i++)
{
for ( var sum = 0; sum <= 10000; sum++)
{
for ( var j = 0; j < k; j++)
{
if (sum >= ratings[i][j])
dp[i][sum]+= dp[i - 1][sum - ratings[i][j]];
}
}
}
for ( var sum = 1; sum <= 10000; sum++)
{
dp[n - 1][sum] += dp[n - 1][sum - 1];
}
for ( var q = 0; q < 2; q++)
{
var a = queries[q][0];
var b = queries[q][1];
document.write(dp[n - 1][b] -
dp[n - 1][a - 1] + " " );
}
}
var ratings = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ];
var queries = [ [ 6, 6 ], [ 1, 6 ] ];
numWays(ratings, queries);
</script>
|
Time Complexity: O(N*maxSum*K), where maxSum is the maximum sum.
Auxiliary Space: O(N*maxSum), where maxSum is the maximum sum.
Efficient approach : Space optimization
In previous approach the dp[i][j] is depend upon the current and previous row of 2D matrix. So to optimize space we use two vectors curr and prev that keep track of current and previous row of DP.
Implementation Steps:
- Initialize two vectors curr and prev to keep track of only current and previous row of Dp with 0.
- Now iterative over subproblems and get the current computation.
- While iteration initialize curr vector.
- Now compute the current value by the help of prev vector and store that value in curr.
- After every iteration store values of curr vector in prev vector for further iteration.
- At last return the answer stored in curr[0].
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
#define n 2
#define k 3
void numWays( int ratings[n][k], int queries[][2])
{
vector< int > prev(10000 + 2);
vector< int > curr(10000 + 2);
for ( int i = 0; i < k; i++)
prev[ratings[0][i]] += 1;
for ( int i = 1; i < n; i++) {
for ( int sum = 0; sum <= 10000; sum++) {
for ( int j = 0; j < k; j++) {
if (sum >= ratings[i][j])
curr[sum] += prev[sum - ratings[i][j]];
}
}
prev = curr;
}
for ( int sum = 1; sum <= 10000; sum++) {
curr[sum] += curr[sum - 1];
}
for ( int q = 0; q < 2; q++) {
int a = queries[q][0];
int b = queries[q][1];
cout << curr[b] - curr[a - 1] << " " ;
}
}
int main()
{
int ratings[n][k] = { { 1, 2, 3 }, { 4, 5, 6 } };
int queries[][2] = { { 6, 6 }, { 1, 6 } };
numWays(ratings, queries);
return 0;
}
|
Python3
n = 2
k = 3
def numWays(ratings, queries):
prev = [ 0 ] * ( 10000 + 2 )
curr = [ 0 ] * ( 10000 + 2 )
for i in range (k):
prev[ratings[ 0 ][i]] + = 1
for i in range ( 1 , n):
for s in range ( 0 , 10001 ):
for j in range (k):
if s > = ratings[i][j]:
curr[s] + = prev[s - ratings[i][j]]
prev = curr.copy()
curr = [ 0 ] * ( 10000 + 2 )
for s in range ( 1 , 10001 ):
prev[s] + = prev[s - 1 ]
for q in range ( 2 ):
a = queries[q][ 0 ]
b = queries[q][ 1 ]
print (prev[b] - prev[a - 1 ], end = ' ' )
if __name__ = = '__main__' :
ratings = [[ 1 , 2 , 3 ], [ 4 , 5 , 6 ]]
queries = [[ 6 , 6 ], [ 1 , 6 ]]
numWays(ratings, queries)
|
C#
using System;
class Program
{
const int n = 2;
const int k = 3;
static void NumWays( int [,] ratings, int [,] queries)
{
int [] prev = new int [10000 + 2];
int [] curr = new int [10000 + 2];
for ( int i = 0; i < k; i++)
{
prev[ratings[0, i]] += 1;
}
for ( int i = 1; i < n; i++)
{
for ( int sum = 0; sum <= 10000; sum++)
{
curr[sum] = 0;
for ( int j = 0; j < k; j++)
{
if (sum >= ratings[i, j])
{
curr[sum] += prev[sum - ratings[i, j]];
}
}
}
Array.Copy(curr, prev, curr.Length);
}
for ( int sum = 1; sum <= 10000; sum++)
{
curr[sum] += curr[sum - 1];
}
for ( int q = 0; q < 2; q++)
{
int a = queries[q, 0];
int b = queries[q, 1];
Console.Write(curr[b] - curr[a - 1] + " " );
}
}
static void Main()
{
int [,] ratings = { { 1, 2, 3 }, { 4, 5, 6 } };
int [,] queries = { { 6, 6 }, { 1, 6 } };
NumWays(ratings, queries);
}
}
|
Javascript
const n = 2;
const k = 3;
function numWays(ratings, queries) {
let prev = new Array(10000 + 2).fill(0);
let curr = new Array(10000 + 2).fill(0);
for (let i = 0; i < k; i++) prev[ratings[0][i]] += 1;
for (let i = 1; i < n; i++) {
for (let sum = 0; sum <= 10000; sum++) {
for (let j = 0; j < k; j++) {
if (sum >= ratings[i][j]) curr[sum] += prev[sum - ratings[i][j]];
}
}
prev = [...curr];
}
for (let sum = 1; sum <= 10000; sum++) {
curr[sum] += curr[sum - 1];
}
for (let q = 0; q < queries.length; q++) {
let a = queries[q][0];
let b = queries[q][1];
console.log(curr[b] - curr[a - 1]);
}
}
const ratings = [
[1, 2, 3],
[4, 5, 6],
];
const queries = [
[6,6],
[1,6]
];
numWays(ratings, queries);
|
Output
2 3
Time Complexity: O(N*maxSum*K), where maxSum is the maximum sum.
Auxiliary Space: O(maxSum)
Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!
Last Updated :
23 Nov, 2023
Like Article
Save Article