Related
So there is a list of points (x1,y1,z1),(x2,y2,z2),...(xn,yn,zn).
You can perform an operation O on any number of these points.
Operation O results in (x,y,z)= (max(x1,x2,...xn),max(y1,y2,...yn),max(z1,z2,...zn)).
Given (x,y,z), you need to determine whether it is possible to perform operation O on some of the points in the list to get (x,y,z) as a result.
For eg: You are given points(1,2,1),(3,1,4),(5,2,1).
Can you perform O operation to get 1) (3,2,1) 2) (1,1,1)
First line contains n and q i.e the number of points and no. of queries
Next n lines contain the n points space seperated
Next q lines contain the q points which are the queries
1<=q<=10^5
1<=n<=10^5
x,y,z are integers
Input:
2 2
1 3 5
5 3 1
5 3 5
3 3 3
Expected Output:
YES
NO
My logic:
for (int i = 0; i < q; i++)
{
cin>>x>>y>>z;
for (int j = 0; j < n; j++)
{
if(arr[j][0]==x && arr[j][1]<=y && arr[j][2]<=z)
first=1;
if(arr[j][0]<=x && arr[j][1]==y && arr[j][2]<=z)
second=1;
if(arr[j][0]<=x && arr[j][1]<=y && arr[j][2]==z)
third=1;
if(first+second+third==3)
break;
}
if(first+second+third==3)
cout<<"YES\n";
else
{
cout<<"NO\n";
}
first=0;
second=0;
third=0;
}
Note: Here arr[][] contains the given coordinates.
for every x y z in queries q I am performing this operation.
Few test cases are failing giving me a runtime error (Time limit Exceeded). Is there a better way to do this.
Your solution is correct but slow, has O(q * n) complexity which is 10^10 at maximum, too much.
I've solved your task using sorting plus merging search, which has O(n log n + q log q) complexity.
The algorithm is as follows:
For each of three cases, (x, y, z), (y, x, z), (z, x, y), signified as coordinates (i0, i1, i2), we do next:
Sort all points and queries by tuple (i0, i1, i2).
Among each equal coordinates i0 compute cumulative minimum of i2.
Merge sorted points and queries next way: For each range of queries and points with equal i0 and i1 take rightmost minimal i2. If such minimal i2 for point is greater than query's i2 then answer for this query is NO, otherwise is probably YES (probably meaning that it should be YES for all 3 orderings of (x, y, z)/(y, x, z)/(z, x, y)).
Basically algorithm above does same thing as your algorithm does, it finds point with equal x, y, or z so that other two coordinates are not greater. But does it through fast algorithm of merging two sorted arrays. So that merging itself takes just O(q + n) time, while whole algorithm time is dominated by sorting algorithms taking O(q log q + n log n) time.
Try it online!
#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
using namespace std;
typedef int CoordT;
typedef tuple<CoordT, CoordT, CoordT> CoordsT;
typedef vector<CoordsT> CoordsVecT;
template <size_t i0, size_t i1, size_t i2>
static void Solve(CoordsVecT const & ps, CoordsVecT const & qs, vector<bool> & yes) {
auto Prep = [&](auto & s, auto const & o){
s.clear();
s.reserve(o.size());
for (size_t i = 0; i < o.size(); ++i)
s.push_back(make_tuple(get<0>(o[i]), get<1>(o[i]), get<2>(o[i]), i));
sort(s.begin(), s.end(), [](auto const & l, auto const & r) -> bool {
return get<i0>(l) < get<i0>(r) || get<i0>(l) == get<i0>(r) && (
get<i1>(l) < get<i1>(r) || get<i1>(l) == get<i1>(r) &&
get<i2>(l) < get<i2>(r)
);
});
};
vector< tuple<CoordT, CoordT, CoordT, size_t> > sps, sqs;
Prep(sps, ps);
Prep(sqs, qs);
vector<CoordT> mins2(sps.size());
CoordT cmin2 = 0;
for (size_t i = 0; i < sps.size(); ++i) {
if (i == 0 || get<i0>(sps[i - 1]) != get<i0>(sps[i]))
cmin2 = get<i2>(sps[i]);
cmin2 = std::min(cmin2, get<i2>(sps[i]));
mins2[i] = cmin2;
}
for (size_t iq = 0, ip = 0; iq < sqs.size(); ++iq) {
auto & cyes = yes[get<3>(sqs[iq])];
if (!cyes)
continue;
while (ip < sps.size() && get<0>(sps[ip]) < get<0>(sqs[iq]))
++ip;
if (ip >= sps.size() || get<0>(sps[ip]) != get<0>(sqs[iq])) {
cyes = false;
continue;
}
while (ip + 1 < sps.size() && get<0>(sps[ip + 1]) == get<0>(sqs[iq]) && get<1>(sps[ip + 1]) <= get<1>(sqs[iq]))
++ip;
if (ip >= sps.size() || get<1>(sps[ip]) > get<1>(sqs[iq]) || mins2[ip] > get<2>(sqs[iq])) {
cyes = false;
continue;
}
}
}
int main() {
size_t n = 0, q = 0;
cin >> n >> q;
auto Input = [](CoordsVecT & v, size_t cnt) {
v.reserve(v.size() + cnt);
for (size_t i = 0; i < cnt; ++i) {
CoordT x, y, z;
cin >> x >> y >> z;
v.push_back(make_tuple(x, y, z));
}
};
CoordsVecT ps, qs;
Input(ps, n);
Input(qs, q);
vector<bool> yes(qs.size(), true);
Solve<0, 1, 2>(ps, qs, yes);
Solve<1, 0, 2>(ps, qs, yes);
Solve<2, 0, 1>(ps, qs, yes);
for (size_t i = 0; i < qs.size(); ++i)
cout << (yes[i] ? "YES" : "NO") << endl;
return 0;
}
Input:
2 2
1 3 5
5 3 1
5 3 5
3 3 3
Output:
YES
NO
I've been tasked to write a partition function for a randomised quicksort with few elements (optimising it by including 3 partitions instead of 2). I've tried implementing my version of it, and have found that it doesn't pass the test cases.
However, by using a classmates' version of partition, it seems to work. Conceptually, I don't see the difference between his and mine, and I can't tell what is it with my version that causes it to break. I wrote it with the concept as him (I think), which involves using counters (j and k) to partition the arrays into 3.
I would greatly appreciate anybody that could point out why mine doesn't work, and what I should do to minimise the chances of these again. I feel like this learning point will be important to me as a developer, thank you!
For comparison, there will be 3 blocks of code, the snippet directly below will be my version of partition, following which will be my classmates' version and lastly will be the actual algorithm which runs our partition.
My version (Does not work)
vector<int> partition2(vector<int> &a, int l, int r) {
int x = a[l];
int j = l;
int k = r;
vector<int> m(2);
// I've tried changing i = l + 1
for (int i = l; i <= r; i++) {
if (a[i] < x) {
swap(a[i], a[j]);
j++;
}
else if (a[i] > x) {
swap(a[i], a[k]);
k--;
}
}
// I've tried removing this
swap(a[l], a[j]);
m[0] = j - 1;
m[1] = k + 1;
return m;
}
My classmates' (which works)
vector<int> partition2(vector<int> &a, int l, int r) {
int x = a[l];
int p_l = l;
int i = l;
int p_e = r;
vector<int> m(2);
while (i <= p_e) {
if (a[i] < x) {
swap(a[p_l], a[i]);
p_l++;
i++;
} else if (a[i] == x) {
i++;
} else {
swap(a[i], a[p_e]);
p_e -= 1;
}
m[0] = p_l - 1;
m[1] = p_e + 1;
}
return m;
}
Actual quick sort algorithm
void randomized_quick_sort(vector<int> &a, int l, int r) {
if (l >= r) {
return;
}
int k = l + rand() % (r - l + 1);
swap(a[l], a[k]);
vector<int> m = partition2(a, l, r);
randomized_quick_sort(a, l, m[0]);
randomized_quick_sort(a, m[1], r);
}
The difference between the two functions for three-way partition is that your code advances i in each pass through the loop, but your classmate's function advances i only when the value at position i is less or equal to the pivot.
Let's go through an example array. The first value, 3, is the pivot. The letters indicate the positions of the variables after each pass through the loop.
j k
3 1 5 2 4
i
The next value is smaller: swap it to the left side and advance j:
j k
1 3 5 2 4
i
The next value, 5, is greater, so it goes to the right:
j k
1 3 4 2 5
i
That's the bad move: Your i has now skipped over the 4, which must go to the right part, too. Your classmate's code does not advance the i here and catches the 4 in the next pass.
Your loop has some invariants, things that must be true after all passes:
All items with an index lower than i are smaller than the pivot.
All items with an index greater than k are greater than the pivot.
All items with an index from j to i - 1 are equal to the pivot.
All items from i to k have not yet been processed.
You can also determine the loop conditions from that:
The pivot is the leftmost element by definition, because the quicksort function swaps it there. It must belong to the group of elements that are equal to the pivot, so you can start your loop at l + 1.
All items starting from k are already in the correct part of the array. That means that you can stop when i reaches k. Going further will needlessly swap elements around inside the "greater than" partition and also move k, which will return wrong partition boundaries.
I'm trying to find the smallest number in a 2D (pointer to pointer) array using recursion, here is the code so far:
int smallest(int **arr, int row_size, int column_size)
{
if (row_size == 0 && column_size == 0)
{
return *(*(arr + 0) + 0);
}
row_size--;
column_size--;
if ((*(*(arr + 0) + 0)) < (*(*arr + row_size) + column_size))
{
return smallest(arr, row_size, column_size);
}
else
{
*(*(arr + 0) + 0) = *(*(arr + row_size) + column_size);
return smallest(arr, row_size, column_size);
}
}
This works but has 2 drawbacks:
1- Only processes square arrays.
2- Only processes indexes where row & column numbers are the same (such as 1,1 2,2 & 3,3 etc.)
I'm trying to make it process non-square arrays as well but that would require decrementing rows and column numbers at different times, I'm not sure how to go about that. Any clues?
Bored professionals overkill introductory problems, volume 3! This version:
Uses a secondary function to traverse each row (that might be off-limits of the question's rules, but you can combine them and switch on whether the pointers array has only one row)
Works with empty arrays (and returns the maximal value of an int)
Does not use index notation
Is purely recursive
int smallest(int const * const array, int const length) {
return length == 0
? std::numeric_limits<int>::max()
: std::min(*array, smallest(array + 1, length - 1));
}
int smallest(int const * const * const array, int const rows, int const columns) {
return rows == 0
? std::numeric_limits<int>::max()
: std::min(
smallest(*array, columns), // Minimum from the current row
smallest(array + 1, rows - 1, columns) // Minimum from the rest of the rows
);
}
See it live on Coliru
not tested, but I think it would work.
#include <algorithm>
#include <limits>
//call with row = row_size-1, column = column_size -1
int smallest(int **arr, int row, int column)
{
if( row<0 || column<0 )return std::numeric_limits<int>::max();
return std::min( {arr[row][column], smallest(arr,row-1,column), smallest(arr,row,column-1)} );
}
The recursion is
Assumed requirements:
Cannot modify function signature (can be simpler if this is not the case)
Can only have a single function (ditto)
Must be purely recursive (i.e. no loops)
Must use pointer dereferencing (*(arr + 1) instead of arr[1])
Procedure:
Recurse through the first row only (setting rows to 1)
Recurse through all other rows (decrement rows)
Code:
int smallest(int **arr, int rows, int columns)
{
// invalid input
if (rows <= 0 || columns <= 0)
return INT_MAX;
// top-left element
int m = **arr;
// single row recursion
if (rows == 1) {
*arr++; // increment the temporary pointer from below
return min(m, smallest(arr, 1, columns - 1));
}
// create a temporary pointer to the first row
// and start recursion in this single row
int* row = *arr;
m = min(m, smallest(&row, 1, columns));
// rest of array
return min(m, smallest(arr + 1, rows, columns - 1));
}
EDIT: since you are allowed to modify the function signature, you can add an extra index for looping through each row; this removes the need for that extra temporary pointer.
int smallest(int **arr, int rows, int columns, int column_index = 0)
{
// invalid input
if (rows <= 0 || column_index >= columns)
return INT_MAX;
// single row recursion
if (rows == 1)
return min(*(*arr + column_index), // first element of row
smallest(arr, 1, columns - 1,
column_index + 1)); // rest of row
// rest of array
return min(smallest(arr, 1, columns), // start on first row
smallest(arr + 1, rows - 1, columns));
}
Note that external call signatures can still be the same as before due to the default argument value.
You'll need to pass the matrix bounds to the function so you can "move" in one dimension when you reach the end of the other.
Something like this (I find it more natural to count upwards than downwards):
int smallest(int **arr, int row, int column, int row_last, int column_last)
{
if (row == row_last && column == column_last)
{
return arr[row][column];
}
else if (column == column_last)
{
return min(arr[row][column], smallest(arr, row+1, 0, row_last, column_last));
}
else
{
return min(arr[row][column], smallest(arr, row, column+1, row_last, column_last));
}
}
(This is one of those exercises that introduces potentially useful concepts in a situation where they don't serve any purpose. It's not a good use of either recursion or pointer arithmetic.)
Though you can get away without recursion, if this is a purely learning excercise for recursion you could try DFS approach.
Have an entry point to start your search (i,j), from each point exhaust all possible areas you could go, (i+1,j)(i-1,j)(i,j+1)(i,j-1). Each recursion step would also check if you are in within the limits of the array which would be i < 0 || i >= rows || j < 0 || j >= cols || visited[i][j] you would just return at this point.
Notice the visited array. So at each point in the recursion there could be a chance that you return back to the same cell right? that would make it an infinite recrusion to avoid this, you track which cells you have already visited, visited[i][j]. Mark them. Each recrusion call keep track of the minimum element recorded by reference, which will be your minimum element.
void findMinElement(vector<vector<int>>& array, int& minelement, int i, int j, vector<vector<bool>>& visited)
{
if(i < 0 || i >= array.size() || j < 0 || j >= array[0].size() || visited[i][j]) return;
minelement = min(minelement,array[i][j]); // is this the best minimum?
visited[i][j] = true; // done visiting this cell
//lets visits others
findMinElement(array, minelement, i+1, j, visited);
findMinElement(array, minelement, i-1, j, visited);
findMinElement(array, minelement, i, j+1, visited);
findMinElement(array, minelement, i, j-1, visited);
}
int main()
{
vector<vector<int>> array =
{
{9,8,6,4},
{13,4,6,11},
{3,8,3,100}
};
int minElement = INT32_MAX;
//same dimensions as array
vector<vector<bool>> visited(array.size(), vector<bool>(array[0].size(),false));
//start from (0,0)
findMinElement(array,minElement,0,0,visited);
cout << minElement;
}
This now gives you the flexibility to start searching from any location in the array as well. It might be a bit to take in, but it will help you with many problems further down the line.
I couldn't help myself. This is an implementation that will divide the input array into four smaller arrays and calls iself on them (unless they are zero size):
int smallest(int **arr, int row_begin, int row_end, int column_begin, int column_end)
{
if (row_end - row_begin == 1 && column_end - column_begin == 1) return *(*(arr + row_begin) + column_begin);
int row_mid = (row_begin + row_end) / 2;
int column_mid = (column_begin + column_end) / 2;
int minimum = smallest(arr, row_mid, row_end, column_mid, column_end);
if (column_mid > column_begin) {
int m = smallest(arr, row_mid, row_end, column_begin, column_mid);
if (m < minimum) minimum = m;
}
if (row_mid > row_begin) {
int m = smallest(arr, row_begin, row_mid, column_begin, column_end);
if (m < minimum) minimum = m;
}
if (column_mid > column_begin && row_mid > row_begin) {
int m = smallest(arr, row_begin, row_mid, column_begin, column_mid);
if (m < minimum) minimum = m;
}
return minimum;
}
Call it like this:
cout << smallest(test_array, 0, rows, 0, columns);
I would do something like:
int smallest(int **arr, int row_size, int column_size)
{
if (row_size > 0 && column_size > 0)
{
int minRow = arr[0][0];
for (int j = 0; j < row_size; ++j)
{
const int *row = arr[j];
minRow = std::min(minRow, *std::min_element(row, row + column_size));
}
return minRow;
}
else return INT_MAX;
}
Hexagonal grid is represented by a two-dimensional array with R rows and C columns. First row always comes "before" second in hexagonal grid construction (see image below). Let k be the number of turns. Each turn, an element of the grid is 1 if and only if the number of neighbours of that element that were 1 the turn before is an odd number. Write C++ code that outputs the grid after k turns.
Limitations:
1 <= R <= 10, 1 <= C <= 10, 1 <= k <= 2^(63) - 1
An example with input (in the first row are R, C and k, then comes the starting grid):
4 4 3
0 0 0 0
0 0 0 0
0 0 1 0
0 0 0 0
Simulation: image, yellow elements represent '1' and blank represent '0'.
This problem is easy to solve if I simulate and produce a grid each turn, but with big enough k it becomes too slow. What is the faster solution?
EDIT: code (n and m are used instead R and C) :
#include <cstdio>
#include <cstring>
using namespace std;
int old[11][11];
int _new[11][11];
int n, m;
long long int k;
int main() {
scanf ("%d %d %lld", &n, &m, &k);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) scanf ("%d", &old[i][j]);
}
printf ("\n");
while (k) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int count = 0;
if (i % 2 == 0) {
if (i) {
if (j) count += old[i-1][j-1];
count += old[i-1][j];
}
if (j) count += (old[i][j-1]);
if (j < m-1) count += (old[i][j+1]);
if (i < n-1) {
if (j) count += old[i+1][j-1];
count += old[i+1][j];
}
}
else {
if (i) {
if (j < m-1) count += old[i-1][j+1];
count += old[i-1][j];
}
if (j) count += old[i][j-1];
if (j < m-1) count += old[i][j+1];
if (i < n-1) {
if (j < m-1) count += old[i+1][j+1];
count += old[i+1][j];
}
}
if (count % 2) _new[i][j] = 1;
else _new[i][j] = 0;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) old[i][j] = _new[i][j];
}
k--;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
printf ("%d", old[i][j]);
}
printf ("\n");
}
return 0;
}
For a given R and C, you have N=R*C cells.
If you represent those cells as a vector of elements in GF(2), i.e, 0s and 1s where arithmetic is performed mod 2 (addition is XOR and multiplication is AND), then the transformation from one turn to the next can be represented by an N*N matrix M, so that:
turn[i+1] = M*turn[i]
You can exponentiate the matrix to determine how the cells transform over k turns:
turn[i+k] = (M^k)*turn[i]
Even if k is very large, like 2^63-1, you can calculate M^k quickly using exponentiation by squaring: https://en.wikipedia.org/wiki/Exponentiation_by_squaring This only takes O(log(k)) matrix multiplications.
Then you can multiply your initial state by the matrix to get the output state.
From the limits on R, C, k, and time given in your question, it's clear that this is the solution you're supposed to come up with.
There are several ways to speed up your algorithm.
You do the neighbour-calculation with the out-of bounds checking in every turn. Do some preprocessing and calculate the neighbours of each cell once at the beginning. (Aziuth has already proposed that.)
Then you don't need to count the neighbours of all cells. Each cell is on if an odd number of neighbouring cells were on in the last turn and it is off otherwise.
You can think of this differently: Start with a clean board. For each active cell of the previous move, toggle the state of all surrounding cells. When an even number of neighbours cause a toggle, the cell is on, otherwise the toggles cancel each other out. Look at the first step of your example. It's like playing Lights Out, really.
This method is faster than counting the neighbours if the board has only few active cells and its worst case is a board whose cells are all on, in which case it is as good as neighbour-counting, because you have to touch each neighbours for each cell.
The next logical step is to represent the board as a sequence of bits, because bits already have a natural way of toggling, the exclusive or or xor oerator, ^. If you keep the list of neigbours for each cell as a bit mask m, you can then toggle the board b via b ^= m.
These are the improvements that can be made to the algorithm. The big improvement is to notice that the patterns will eventually repeat. (The toggling bears resemblance with Conway's Game of Life, where there are also repeating patterns.) Also, the given maximum number of possible iterations, 2⁶³ is suspiciously large.
The playing board is small. The example in your question will repeat at least after 2¹⁶ turns, because the 4×4 board can have at most 2¹⁶ layouts. In practice, turn 127 reaches the ring pattern of the first move after the original and it loops with a period of 126 from then.
The bigger boards may have up to 2¹⁰⁰ layouts, so they may not repeat within 2⁶³ turns. A 10×10 board with a single active cell near the middle has ar period of 2,162,622. This may indeed be a topic for a maths study, as Aziuth suggests, but we'll tacke it with profane means: Keep a hash map of all previous states and the turns where they occurred, then check whether the pattern has occurred before in each turn.
We now have:
a simple algorithm for toggling the cells' state and
a compact bitwise representation of the board, which allows us to create a hash map of the previous states.
Here's my attempt:
#include <iostream>
#include <map>
/*
* Bit representation of a playing board, at most 10 x 10
*/
struct Grid {
unsigned char data[16];
Grid() : data() {
}
void add(size_t i, size_t j) {
size_t k = 10 * i + j;
data[k / 8] |= 1u << (k % 8);
}
void flip(const Grid &mask) {
size_t n = 13;
while (n--) data[n] ^= mask.data[n];
}
bool ison(size_t i, size_t j) const {
size_t k = 10 * i + j;
return ((data[k / 8] & (1u << (k % 8))) != 0);
}
bool operator<(const Grid &other) const {
size_t n = 13;
while (n--) {
if (data[n] > other.data[n]) return true;
if (data[n] < other.data[n]) return false;
}
return false;
}
void dump(size_t n, size_t m) const {
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < m; j++) {
std::cout << (ison(i, j) ? 1 : 0);
}
std::cout << '\n';
}
std::cout << '\n';
}
};
int main()
{
size_t n, m, k;
std::cin >> n >> m >> k;
Grid grid;
Grid mask[10][10];
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < m; j++) {
int x;
std::cin >> x;
if (x) grid.add(i, j);
}
}
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < m; j++) {
Grid &mm = mask[i][j];
if (i % 2 == 0) {
if (i) {
if (j) mm.add(i - 1, j - 1);
mm.add(i - 1, j);
}
if (j) mm.add(i, j - 1);
if (j < m - 1) mm.add(i, j + 1);
if (i < n - 1) {
if (j) mm.add(i + 1, j - 1);
mm.add(i + 1, j);
}
} else {
if (i) {
if (j < m - 1) mm.add(i - 1, j + 1);
mm.add(i - 1, j);
}
if (j) mm.add(i, j - 1);
if (j < m - 1) mm.add(i, j + 1);
if (i < n - 1) {
if (j < m - 1) mm.add(i + 1, j + 1);
mm.add(i + 1, j);
}
}
}
}
std::map<Grid, size_t> prev;
std::map<size_t, Grid> pattern;
for (size_t turn = 0; turn < k; turn++) {
Grid next;
std::map<Grid, size_t>::const_iterator it = prev.find(grid);
if (1 && it != prev.end()) {
size_t start = it->second;
size_t period = turn - start;
size_t index = (k - turn) % period;
grid = pattern[start + index];
break;
}
prev[grid] = turn;
pattern[turn] = grid;
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < m; j++) {
if (grid.ison(i, j)) next.flip(mask[i][j]);
}
}
grid = next;
}
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < m; j++) {
std::cout << (grid.ison(i, j) ? 1 : 0);
}
std::cout << '\n';
}
return 0;
}
There is probably room for improvement. Especially, I'm not so sure how it fares for big boards. (The code above uses an ordered map. We don't need the order, so using an unordered map will yield faster code. The example above with a single active cell on a 10×10 board took significantly longer than a second with an ordered map.)
Not sure about how you did it - and you should really always post code here - but let's try to optimize things here.
First of all, there is not really a difference between that and a quadratic grid. Different neighbor relationships, but I mean, that is just a small translation function. If you have a problem there, we should treat this separately, maybe on CodeReview.
Now, the naive solution is:
for all fields
count neighbors
if odd: add a marker to update to one, else to zero
for all fields
update all fields by marker of former step
this is obviously in O(N). Iterating twice is somewhat twice the actual run time, but should not be that bad. Try not to allocate space every time that you do that but reuse existing structures.
I'd propose this solution:
at the start:
create a std::vector or std::list "activated" of pointers to all fields that are activated
each iteration:
create a vector "new_activated"
for all items in activated
count neighbors, if odd add to new_activated
for all items in activated
set to inactive
replace activated by new_activated*
for all items in activated
set to active
*this can be done efficiently by putting them in a smart pointer and use move semantics
This code only works on the activated fields. As long as they stay within some smaller area, this is far more efficient. However, I have no idea when this changes - if there are activated fields all over the place, this might be less efficient. In that case, the naive solution might be the best one.
EDIT: after you now posted your code... your code is quite procedural. This is C++, use classes and use representation of things. Probably you do the search for neighbors right, but you can easily make mistakes there and therefore should isolate that part in a function, or better method. Raw arrays are bad and variables like n or k are bad. But before I start tearing your code apart, I instead repeat my recommendation, put the code on CodeReview, having people tear it apart until it is perfect.
This started off as a comment, but I think it could be helpful as an answer in addition to what has already been stated.
You stated the following limitations:
1 <= R <= 10, 1 <= C <= 10
Given these restrictions, I'll take the liberty to can represent the grid/matrix M of R rows and C columns in constant space (i.e. O(1)), and also check its elements in O(1) instead of O(R*C) time, thus removing this part from our time-complexity analysis.
That is, the grid can simply be declared as bool grid[10][10];.
The key input is the large number of turns k, stated to be in the range:
1 <= k <= 2^(63) - 1
The problem is that, AFAIK, you're required to perform k turns. This makes the algorithm be in O(k). Thus, no proposed solution can do better than O(k)[1].
To improve the speed in a meaningful way, this upper-bound must be lowered in some way[1], but it looks like this cannot be done without altering the problem constraints.
Thus, no proposed solution can do better than O(k)[1].
The fact that k can be so large is the main issue. The most anyone can do is improve the rest of the implementation, but this will only improve by a constant factor; you'll have to go through k turns regardless of how you look at it.
Therefore, unless some clever fact and/or detail is found that allows this bound to be lowered, there's no other choice.
[1] For example, it's not like trying to determine if some number n is prime, where you can check all numbers in the range(2, n) to see if they divide n, making it a O(n) process, or notice that some improvements include only looking at odd numbers after checking n is not even (constant factor; still O(n)), and then checking odd numbers only up to √n, i.e., in the range(3, √n, 2), which meaningfully lowers the upper-bound down to O(√n).
I was trying to do this question i came across while looking up interview questions. We are asked the number of ways of placing r coins on a n*m grid such that each row and col contain at least one coin.
I thought of a backtracking solution, processing each cell in the grid in a row major order, I have set up my recursion in this way. Seems my approach is faulty because it outputs 0 every time. Could someone please help me find the error in my approach. ? Thanks.
constraints. n , m < 200 and r < n*m;
Here is the code i came up with.
#include<cstdio>
#define N 201
int n, m , r;
int used[N][N];
int grid[N][N] ; // 1 is coin is placed . 0 otherwise. // -1 undecided.
bool isOk()
{
int rows[N];
int cols[N];
for(int i = 0 ; i < n ; i++) rows[i] = 0;
for(int i = 0 ; i < m ; i++) cols[i] = 0;
int sum = 0;
for(int i = 0 ; i < n ; i++)for(int j = 0; j < m ; j++)
{
if(grid[i][j]==1)
{
rows[i]++;
cols[j]++;
sum++;
}
}
for(int i = 0 ; i < n ; i++)
{
if(rows[i]==0) return false;
}
for(int j = 0 ; j < n ; j++)
{
if(cols[j]==0) return false;
}
if(sum==r) return true;
else return false;
}
int calc_ways(int row , int col, int coins)
{
if(row >= n) return 0;
if(col >= m) return 0;
if(coins > r) return 0;
if(coins == r)
{
bool res = isOk();
if(res) return 1;
else 0;
}
if(row == n - 1 and col== m- 1)
{
bool res = isOk();
if(res) return 1;
else return 0;
}
int nrow, ncol;
if(col + 1 >= m)
{
nrow = row + 1;
ncol = 0;
}
else
{
nrow = row;
ncol = col + 1;
}
if(used[row][col]) return calc_ways(nrow, ncol, coins);
int ans = 0;
used[row][col] = 1;
grid[row][col] = 0;
ans += calc_ways(nrow , ncol , coins);
grid[row][col] = 1;
ans += calc_ways(nrow , ncol , coins + 1);
return ans;
}
int main()
{
int t;
scanf("%d" , &t);
while(t--)
{
scanf("%d %d %d" , &n , &m , &r);
for(int i = 0 ; i <= n ; i++)
{
for(int j = 0; j <= m ; j++)
{
used[i][j] = 0;
grid[i][j] = -1;
}
}
printf("%d\n" , calc_ways(0 , 0 , 0 ));
}
return 0;
}
You barely need a program to solve this at all.
Without loss of generality, let m <= n.
To begin with, we must have n <= r, otherwise no solution is possible.
Then, we subdivide the problem into a square of size m x m, on to which we will place m coins along the major diagonal, and a remainder, on to which we will place n - m coins so as to fulfil the remaining condition.
There is one way to place the coins along the major diagonal of the square.
There are m^(n - m) possibilities for the remainder.
We can permute the total so far in n! ways, although some of those will be duplicates (how many is left as an exercise for the student).
Furthermore, there are r - n coins left to place and (m - 1)n places left to put them.
Putting these all together we have an upper bound of
1 x m^(n - m) x n! x C((m - 1)n, r - n)
solutions to the problem. Divide this number by the number of duplicate permutations and you're done.
Problem 1
The code will start by placing a coin on each square and marking each square as used.
It will then test the final position and decide that the final position does not meet the goal of r coins.
Next it will start backtracking, but will never actually try another choice because used[row][col] is set to 1 and this shortcircuits the code to place coins.
In other words, one problem is that entries in "used" are set, but never cleared during the recursion.
Problem 2
Another problem with the code is that if n,m are of size 200, then it will never complete.
The issue is that this backtracking code has complexity O(2^(n*m)) as it will try all possible combinations of placing coins (many universe lifetimes for n=m=200...).
I would recommend you look at a different approach. For example, you might want to consider dynamic programming to compute how many ways there are of placing "k" coins on the remaining "a" columns of the board such that we make sure that we place coins on the "b" rows of the board that currently have no coins.
It can be treated as total ways in which d grid can b filled with r coins -(total ways leaving a single row nd filling in d rest -total ways leaving a single column nd filling in d rest- total ways leaving a row nd column together nd filling d rest) which implies
p(n*m ,r) -( (p((n-1)*m , r) * c(n,1)) +(p((m-1)*n , r) * c(m,1))+(p((n-1)*(m-1) , r) * c(n,1)*c(m,1)) )
I just think so but not sure of it!