Unable to understand the Choice of Area DP - algorithm

Consider a game, in which you have two types of powers, A and B and there are 3 types of Areas X, Y and Z. Every second you have to switch between these areas, each area has specific properties by which your power A and power B increase or decrease. We need to keep choosing areas in such a way that our survival time is maximized. Survival time ends when any of the powers, A or B reaches less than 0.
Examples:
Initial value of Power A = 20
Initial value of Power B = 8
Area X (3, 2) : If you step into Area X,
A increases by 3,
B increases by 2
Area Y (-5, -10) : If you step into Area Y,
A decreases by 5,
B decreases by 10
Area Z (-20, 5) : If you step into Area Z,
A decreases by 20,
B increases by 5
It is possible to choose any area in our first step.
We can survive at max 5 unit of time by following
these choice of areas :
X -> Z -> X -> Y -> X

This problem can be solved using recursion, after each time unit we can go to any of the area but we will choose that area which ultimately leads to maximum survival time. As recursion can lead to solving same subproblem many time, we will memoize the result on basis of power A and B, if we reach to same pair of power A and B, we won’t solve it again instead we will take the previously calculated result.
Given below is the simple implementation of above approach.
// C++ code to get maximum survival time
#include <bits/stdc++.h>
using namespace std;
// structure to represent an area
struct area
{
// increment or decrement in A and B
int a, b;
area(int a, int b) : a(a), b(b)
{}
};
// Utility method to get maximum of 3 integers
int max(int a, int b, int c)
{
return max(a, max(b, c));
}
// Utility method to get maximum survival time
int maxSurvival(int A, int B, area X, area Y, area Z,
int last, map<pair<int, int>, int>& memo)
{
// if any of A or B is less than 0, return 0
if (A <= 0 || B <= 0)
return 0;
pair<int, int> cur = make_pair(A, B);
// if already calculated, return calculated value
if (memo.find(cur) != memo.end())
return memo[cur];
int temp;
// step to areas on basis of last chose area
switch(last)
{
case 1:
temp = 1 + max(maxSurvival(A + Y.a, B + Y.b,
X, Y, Z, 2, memo),
maxSurvival(A + Z.a, B + Z.b,
X, Y, Z, 3, memo));
break;
case 2:
temp = 1 + max(maxSurvival(A + X.a, B + X.b,
X, Y, Z, 1, memo),
maxSurvival(A + Z.a, B + Z.b,
X, Y, Z, 3, memo));
break;
case 3:
temp = 1 + max(maxSurvival(A + X.a, B + X.b,
X, Y, Z, 1, memo),
maxSurvival(A + Y.a, B + Y.b,
X, Y, Z, 2, memo));
break;
}
// store the result into map
memo[cur] = temp;
return temp;
}
// method returns maximum survival time
int getMaxSurvivalTime(int A, int B, area X, area Y, area Z)
{
if (A <= 0 || B <= 0)
return 0;
map< pair<int, int>, int > memo;
// At first, we can step into any of the area
return
max(maxSurvival(A + X.a, B + X.b, X, Y, Z, 1, memo),
maxSurvival(A + Y.a, B + Y.b, X, Y, Z, 2, memo),
maxSurvival(A + Z.a, B + Z.b, X, Y, Z, 3, memo));
}
// Driver code to test above method
int main()
{
area X(3, 2);
area Y(-5, -10);
area Z(-20, 5);
int A = 20;
int B = 8;
cout << getMaxSurvivalTime(A, B, X, Y, Z);
return 0;
}
Output :
5

Related

Random number generator with 3 inputs

I am looking for a random number generator with 3 inputs for a terrain generator. The inputs are an x, y (position) and a seed value. And the function returns back a random number from 0-1.
So far I found this question, but this has 2 inputs. Although I can combine x and y to a single number, to get two inputs, this will restrict the choices I will have for x, y as they need to be sufficiently large (the terrain is infinite).
Is there any random function that takes in 3 inputs, or would I need to end up using the 2 input version?
Something like this should work. It takes three 32-bit integers, and outputs a single 32-bit integer. If you want to turn the output into a double between 0 and 1, just divide by UINT32_MAX.
The input and output sizes can be adjusted.
You can also tweak the balance between output quality and speed. You'll notice that the middle section is just repeated 3 lines, remove or add more of them to make the output more or less biased.
Here's the C code.
uint32_t rotl32(uint32_t n, uint8_t k) {
uint32_t a = n << k;
uint32_t b = n >> (32 - k);
return a | b;
}
uint32_t three_input_random(uint32_t x, uint32_t y, uint32_t z) {
uint32_t a = x;
uint32_t b = y;
uint32_t c = z;
b ^= rotl32(a + c, 7);
c ^= rotl32(b + a, 9);
a ^= rotl32(c + b, 18);
b ^= rotl32(a + c, 7);
c ^= rotl32(b + a, 9);
a ^= rotl32(c + b, 18);
b ^= rotl32(a + c, 7);
c ^= rotl32(b + a, 9);
a ^= rotl32(c + b, 18);
return a + b + c + x + y + z;
}

Counting inversions in a segment with updates

I'm trying to solve a problem which goes like this:
Problem
Given an array of integers "arr" of size "n", process two types of queries. There are "q" queries you need to answer.
Query type 1
input: l r
result: output number of inversions in [l, r]
Query type 2
input: x y
result: update the value at arr [x] to y
Inversion
For every index j < i, if arr [j] > arr [i], the pair (j, i) is one inversion.
Input
n = 5
q = 3
arr = {1, 4, 3, 5, 2}
queries:
type = 1, l = 1, r = 5
type = 2, x = 1, y = 4
type = 1, l = 1, r = 5
Output
4
6
Constraints
Time: 4 secs
1 <= n, q <= 100000
1 <= arr [i] <= 40
1 <= l, r, x <= n
1 <= y <= 40
I know how to solve a simpler version of this problem without updates, i.e. to simply count the number of inversions for each position using a segment tree or fenwick tree in O(N*log(N)). The only solution I have to this problem is O(q*N*log(N)) (I think) with segment tree other than the O(q*N2) trivial algorithm. This however does not fit within the time constraints of the problem. I would like to have hints towards a better algorithm to solve the problem in O(N*log(N)) (if it's possible) or maybe O(N*log2(N)).
I first came across this problem two days ago and have been spending a few hours here and there to try and solve it. However, I'm finding it non-trivial to do so and would like to have some help/hints regarding the same. Thanks for your time and patience.
Updates
Solution
With the suggestion, answer and help by Tanvir Wahid, I've implemented the source code for the problem in C++ and would like to share it here for anyone who might stumble across this problem and not have an intuitive idea on how to solve it. Thank you!
Let's build a segment tree with each node containing information about how many inversions exist and the frequency count of elements present in its segment of authority.
node {
integer inversion_count : 0
array [40] frequency : {0...0}
}
Building the segment tree and handling updates
For each leaf node, initialise inversion count to 0 and increase frequency of the represented element from the input array to 1. The frequency of the parent nodes can be calculated by summing up frequencies of the left and right childrens. The inversion count of parent nodes can be calculated by summing up the inversion counts of left and right children nodes added with the new inversions created upon merging the two segments of their authority which can be calculated using the frequencies of elements in each child. This calculation basically finds out the product of frequencies of bigger elements in the left child and frequencies of smaller elements in the right child.
parent.inversion_count = left.inversion_count + right.inversion_count
for i in [39, 0]
for j in [0, i)
parent.inversion_count += left.frequency [i] * right.frequency [j]
Updates are handled similarly.
Answering range queries on inversion counts
To answer the query for the number of inversions in the range [l, r], we calculate the inversions using the source code attached below.
Time Complexity: O(q*log(n))
Note
The source code attached does break some good programming habits. The sole purpose of the code is to "solve" the given problem and not to accomplish anything else.
Source Code
/**
Lost Arrow (Aryan V S)
Saturday 2020-10-10
**/
#include "bits/stdc++.h"
using namespace std;
struct node {
int64_t inv = 0;
vector <int> freq = vector <int> (40, 0);
void combine (const node& l, const node& r) {
inv = l.inv + r.inv;
for (int i = 39; i >= 0; --i) {
for (int j = 0; j < i; ++j) {
// frequency of bigger numbers in the left * frequency of smaller numbers on the right
inv += 1LL * l.freq [i] * r.freq [j];
}
freq [i] = l.freq [i] + r.freq [i];
}
}
};
void build (vector <node>& tree, vector <int>& a, int v, int tl, int tr) {
if (tl == tr) {
tree [v].inv = 0;
tree [v].freq [a [tl]] = 1;
}
else {
int tm = (tl + tr) / 2;
build(tree, a, 2 * v + 1, tl, tm);
build(tree, a, 2 * v + 2, tm + 1, tr);
tree [v].combine(tree [2 * v + 1], tree [2 * v + 2]);
}
}
void update (vector <node>& tree, int v, int tl, int tr, int pos, int val) {
if (tl == tr) {
tree [v].inv = 0;
tree [v].freq = vector <int> (40, 0);
tree [v].freq [val] = 1;
}
else {
int tm = (tl + tr) / 2;
if (pos <= tm)
update(tree, 2 * v + 1, tl, tm, pos, val);
else
update(tree, 2 * v + 2, tm + 1, tr, pos, val);
tree [v].combine(tree [2 * v + 1], tree [2 * v + 2]);
}
}
node inv_cnt (vector <node>& tree, int v, int tl, int tr, int l, int r) {
if (l > r)
return node();
if (tl == l && tr == r)
return tree [v];
int tm = (tl + tr) / 2;
node result;
result.combine(inv_cnt(tree, 2 * v + 1, tl, tm, l, min(r, tm)), inv_cnt(tree, 2 * v + 2, tm + 1, tr, max(l, tm + 1), r));
return result;
}
void solve () {
int n, q;
cin >> n >> q;
vector <int> a (n);
for (int i = 0; i < n; ++i) {
cin >> a [i];
--a [i];
}
vector <node> tree (4 * n);
build(tree, a, 0, 0, n - 1);
while (q--) {
int type, x, y;
cin >> type >> x >> y;
--x; --y;
if (type == 1) {
node result = inv_cnt(tree, 0, 0, n - 1, x, y);
cout << result.inv << '\n';
}
else if (type == 2) {
update(tree, 0, 0, n - 1, x, y);
}
else
assert(false);
}
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.precision(10);
std::cout << std::fixed << std::boolalpha;
int t = 1;
// std::cin >> t;
while (t--)
solve();
return 0;
}
arr[i] can be at most 40. We can use this to our advantage. What we need is a segment tree. Each node will hold 41 values (A long long int which represents inversions for this range and a array of size 40 for count of each numbers. A struct will do). How do we merge two children of a node. We know inversions for left child and right child. Also know frequency of each numbers in both of them. Inversion of parent node will be summation of inversions of both children plus number of inversions between left and right child. We can easily find inversions between two children from frequency of numbers. Query can be done in similar way. Complexity O(40*qlog(n))

How to find triplets { x , y , z } from three sorted A, B, C such that x < y < z in O(n)?

Suppose I have three sorted arrays
A : { 4, 9 }
B : { 2, 11}
C : { 12, 14}
Then the no of triplets { x, y, z } such that x belongs to A, y belongs to B and z belongs to C such that x < y < z is -> 4
I know O( n ^3 ) algo but how to do it in O(n). Where n is the length of the array.
Initialize a 'count' variable to zero.
Find the lengths of the three lists in linear time, if not given.
Merge the three lists into one sorted list in linear time, keeping track of which of the original lists each belonged to.
Parse the merged list. As you do so, keep track of the number of elements from A you have seen, and from C that you have NOT seen. Each time you encounter a member from list B, increase your count by (A seen) * (C not seen), both as of the current index. What we're doing here is, for ever element from B, counting the number of ways we can find a smaller element from A and a bigger element from C.
You can also keep track of B's and stop after the last one in the merged list.
O(n)
E.g., A : { 4, 9 }
B : { 2, 11}
C : { 12, 14}
(2,B), (4,A), (9,A), (11,B), (12,C), (14,C)
initialize: count = 0, A_seen = 0, C_unseen=2
index 0: A_seen = 0, C_unseen = 2, count = 0 + 0*2 = 0
index 1: A_seen = 1, C unseen = 2, count unchanged
index 2: A_seen = 2, C unseen = 2, count unchanged
index 3: A_seen = 2, C unseen = 2, count = 0 + 2*2 = 4
We can stop here since we're out of B's.
-- edit --
Easy optimization: don't merge the lists, but just iterate through B, keeping track of the index of the largest smaller element of A and smallest larger element of C, then proceed as before. This is still linear and has less overhead.
You can do it using memorize count with binary search.. complexity: O(n * logn).
For each element of B search greater value position from array C. Then you can count no of valid y < z. it will be (n - position of greater value)
Now for each element of A search greater value position from array B. Then you can count no of valid x < y. it will be (n - position of greater value)..Here you need to take sum of count of each valid position of B.
sample code here :
#include <bits/stdc++.h>
using namespace std;
int bs(int l, int h, int v, int A[]) {
int m;
while (l <= h) {
m = (l + h) / 2;
if (A[m] < v) {
l = m + 1;
} else {
h = m - 1;
}
}
return l;
}
int main() {
int A[] = {4,9};
int B[] = {2,11};
int C[] = {12,14};
int dp[] = {0};
int n = 2, i, ans = 0, p;
for (i = 0; i < n; i++) {
p = bs(0, n-1, B[i], C);
dp[i] = i ? dp[i-1] + n-p : n-p;
}
for (i = 0; i < n; i++) {
p = bs(0,n-1, A[i], B);
if (p) {
ans += (dp[n-1]-dp[p-1]);
} else {
ans += dp[n-1];
}
}
printf("%d\n", ans);
return 0;
}

Drawing ellipse with Bresenham's algorithm

Hello,
I'm trying to draw an ellipse, which is parallel to the orthogonal system, using Bresenham's algorithm. I want to draw the top-left (W,SW,S) quarter of the ellipse, and then deduce others.
To do this, i'm using an incremental algorithm with the second-order logic. I did it from another algorithm that draw the top-right quarter first, but what i'm doing isn't working.
The problem appears when the 2nd region is drawing, and I don't know where it comes from.
You can see what I have (black), and what I expect (green):
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+30,yc+20) in this example)
(a is abs(x2-xc), and b is abs(y2-yc))
The first parameter is the middle of the ellipse (xc, yc), the second is the upper right point established the x and y radius. You can see the ellipse goes too far (2 points on the left and on the right). You can see an other example
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+15,yc+18) in this example)
The algorithm is deduced from the incremental algorithm with the second-order logic.
Here is my code, (a is abs(x2-xc), and b is abs(y2-yc))
ellipse(int a, int b, int xc, int yc) {
int a2 = a*a, b2 = b*b;
int x = 0, y = b; //Starting point
int incSW = b2*2 + a2*2;
int deltaW = b2*(-2*x + 3); //deduced from incremental algorithm with the second-order logic
int deltaS = a2*(-2*y + 3);
int deltaSW = deltaW + deltaS;
int d1 = b2 - a2*b + a2/4; //dp starting value in the first region
int d2 = b2*(x - 0.5)*(x - 0.5) + a2*(y - 1)*(y - 1) - a2*b2; //dp starting value in the second region
//First region
while(a2*(y-0.5) >= b2*(-x-1)) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d1>0) {
d1+=deltaSW;
deltaW+=b2*2;
deltaSW+=incSW;
y--;
}
else {
d1+=deltaW;
deltaW+=2*b2;
deltaSW+=2*b2;
}
x--;
}
deltaSW = b2*(2 - 2*x) + a2*(-2*y + 3);
//Second region
while(y>=0) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d2>0) {
d2+=deltaS;
deltaS+=a2*2;
deltaSW+=a2*2;
}
else {
d2+=deltaSW;
deltaSW+=incSW;
deltaS+=a2*2;
x--;
}
y--;
}
}
I hope you can help me, thanks.
Using the error term e = a x^2 + b y^2 - r^2, it's pretty easy to show that a step from (x,y) to (x,y+1) changes the error by 2by + b, a step to (x+1,y+1) by 2ax + a + 2by + b, and a step to (x+1,y) by 2ax + a.
Starting from a point (-x0, 0), choose the least absolute error step from these three. The first two cases are the norm for the "first region" as you call it.
The first time a step right, (x,y) to (x+1,y), produces least error, you know you're in the second region. At this point the first case is no longer needed. The quarter ellipse can be finished using only the second two cases.
Note this check avoids the floating point operations you've used. The whole point of Bresenham-ish algorithms is to avoid floating point.
The last bit to notice is that you don't want to compute 2ax or 2by each iteration. The multiplications can be avoided by maintaining variables, say dx=2ax and dy=2by, and updating them. A step from x to x+1 increments dx by 2a, a constant. Similarly a step from y to y+1 increments dy by 2b.
Putting all this together, you get the (rough) code below.
Note that you can check the incremental error computation by verifying it against the original error term. If (x0,0) is the initial point, then you know x0^2 = r^2. So the actual error in every iteration is a * x^2 + b * y^2 - x0^2. This ought to equal e in the code below, and it does.
import static java.lang.Math.abs;
import java.util.Arrays;
import java.util.function.BiConsumer;
public class EllipseTracer {
static char [] [] raster = new char[51][101];
static void trace(int x, int y, int a, int b, BiConsumer<Integer, Integer> emitter) {
emitter.accept(x, y);
int e = 0;
int dx = 2 * a * x;
int dy = 2 * b * y;
// First region: stepping north and northeast.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eUp = e + dyb;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eUp) < abs(eDg)) {
emitter.accept(x, ++y);
e = eUp;
dy += 2 * b;
} else {
if (abs(eRt) < abs(eDg)) {
// Step east is least error. Found second region.
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
break;
}
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
// Second region: step northeast and east.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eRt) < abs(eDg)) {
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
} else {
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
}
static void emit(int x, int y) {
raster[y][x + 100] = '*';
}
public static void main(String [] args) {
for (int i = 0; i < raster.length; ++i) {
Arrays.fill(raster[i], ' ');
}
trace(-100, 0, 1, 4, EllipseTracer::emit);
for (int i = 0; i < raster.length; ++i) {
System.out.println(raster[i]);
}
}
}
You can add more tricks to avoid the absolute values, but I'll let you look for those.

What's algorithm used to solve Linear Diophantine equation: ax + by = c

I'm looking for integers solution here. I know it has infinitely many solution derived from the first pair solution and gcd(a,b)|c. However, how could we find the first pair of solution? Is there any algorithm to solve this problem?
Thanks,
Chan
Note that there isn't always a solution. In fact, there's only a solution if c is a multiple of gcd(a, b).
That said, you can use the extended euclidean algorithm for this.
Here's a C++ function that implements it, assuming c = gcd(a, b). I prefer to use the recursive algorithm:
function extended_gcd(a, b)
if a mod b = 0
return {0, 1}
else
{x, y} := extended_gcd(b, a mod b)
return {y, x-(y*(a div b))}
int ExtendedGcd(int a, int b, int &x, int &y)
{
if (a % b == 0)
{
x = 0;
y = 1;
return b;
}
int newx, newy;
int ret = ExtendedGcd(b, a % b, newx, newy);
x = newy;
y = newx - newy * (a / b);
return ret;
}
Now if you have c = k*gcd(a, b) with k > 0, the equation becomes:
ax + by = k*gcd(a, b) (1)
(a / k)x + (b / k)y = gcd(a, b) (2)
So just find your solution for (2), or alternatively find the solution for (1) and multiply x and y by k.

Resources