This question was asked in a forum. Any suggestions?
There is a pyramid with 1 cup at level , 2 at level 2 , 3 at level 3 and so on.. It looks something like this
The pascal triangle solution for calculating binomial coefficient can be used to solve this problem. We just need to tweak the algorithm a little bit and instead of calculating binomial coefficients, we calculate the water level. Given ith cup, we calculate level and index to find out where the cup sits in the triangle.
The cups are modelled as
0 Level 1
1 2 Level 2
3 4 5 Level 3
getIndex() and getLevel() returns the index and level. Index and Level starts at 1.
public static int getIndex(int i) {
int totalNodes = i + 1;
double d = (-3 + Math.sqrt(9 - 8*(1-totalNodes)))/2;
int level = (int)Math.floor(d);
int total = ((level+1)*(level+2))/2;
int index = 0;
if(total==totalNodes) index = level;
else{
level++;
index = totalNodes - total - 1;
}
return ++index;
}
public static int getLevel(int i) {
int totalNodes = i + 1;
double d = (-3 + Math.sqrt(9 - 8*(1-totalNodes)))/2;
int level = (int)Math.floor(d);
int total = ((level+1)*(level+2))/2;
int index = 0;
if(total==totalNodes) index = level;
else{
level++;
index = totalNodes - total - 1;
}
return ++level;
}
k is kth cup starting at 0. C is cup capacity, L is total water.
public static double getWaterLevel(double C, double L, int k) {
int n = getLevel(k);
int index = getIndex(k);
double[] water = new double[index+1];
water[1] = L;
for(int i = 2; i <= n; i++)
{
boolean overflowed = false;
for(int j = Math.min(i, index); j > 0; j--) {
double over = 0;
if(water[j]>C) over = (water[j]-C)/2;
if(water[j-1]>C) over += (water[j-1]-C)/2;
water[j] = over;
if(!overflowed && over!=0) overflowed=true;
}
if(!overflowed) break; // no more overflow. stop
}
return water[index] > C ? C : water[index];
}
Here is a simple and comprehensible implementation:
public class main {
static float total_water = 50;
static int N = 20;
static glass[][] pyramid = new glass[N][N];
public static void main(String[] args) {
build_pyramid();
pour_water(0, 0, total_water);
print_pyramid();
print_total_water_stored();
}
private static void print_total_water_stored() {
float total = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j <= i; j++)
total += pyramid[i][j].filled;
}
System.out.println("total water stored= " + total);
}
private static void pour_water(int row, int col, float water) {
if (water >= (pyramid[row][col].capacity - pyramid[row][col].filled)) {
water -= (pyramid[row][col].capacity - pyramid[row][col].filled);
pyramid[row][col].filled = pyramid[row][col].capacity;
pour_water(row + 1, col, water / 2);
pour_water(row + 1, col + 1, water / 2);
} else {
pyramid[row][col].filled += water;
}
}
public static void build_pyramid() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
pyramid[i][j] = new glass(1);
}
}
public static void print_pyramid() {
for (int i = 0; i < N; i++) {
for (int j = 0; j <= i; j++)
System.out.print(pyramid[i][j].filled + " ");
System.out.println();
}
}
}
class glass {
float capacity;
float filled;
glass(float cap) {
capacity = cap;
filled = 0;
}
}
Each glass has an incoming flow, an amount of water in the glass, and maybe some outgoing flow (overflow).
If each glass can contain 1 unit of water, and you pour 15 units of water, you get the following (overflow amount in parenthesis):
Incoming flow = 15, capacity = 1
Level 1: 1(14)
Level 2: 1(6) 1(6)
Level 3: 1(2) 1(5) 1(2)
Level 4: 1(1) 1(2.5) 1(2.5) 1(1)
Level 5: 1 1(0.75) 1(1.5) 1(0.75) 1
Level 6: 0 0.375 1(0.125) 1(0.125) 0.375 0
Level 7: 0 0 0.0625 0.125 0.0625 0 0
The incoming flow to the first level is L. The incoming flow from glass c
on level r
is Fin(c, r)
, and could be written as:
Fin(0, r) = 0
Fin(r+1, r) = 0
Fin(1, 1) = L
Fin(c, r) = Fout(c - 1, r - 1)/2 + Fout(c, r - 1)/2
The amount of water in that glass is:
A(c, r) = Min(C, Fin(c, r))
And the outgoing flow is:
Fout(c, r) = Max(0, Fin(c, r) - C)
I don't see any obvious formula for evaluating A(c, r)
without doing it recursively.
To get from an index to a row and glass position, you can do:
index = r*(r-1)/2 + c
r = floor((1 + sqrt(8*index - 7))/2)
c = index - r*(r-1)/2
(indexes start with 1)
Some ideas: (1) The important is knowing which two cups are inputs to the ith cup. (2) The important is know the Minimum Lleft that will bring you water from your left side and what level Lright will bring you water from your right side (3) So you need to know which cups provide water to cup ith. This is easier, thinking quick, if you start numbering from 0. Cup ith will fill (i-1)*2+1 and i*2, what means that cup kth will receive water from (for k%2=1) (k-1)/2 and (k+1)/2 (for k%2=0) k/2 and k/2+1 (4) With that you should check that for any L you will calculate the difference L-Lleft and L-Lright. When positive the water provided is the result of dividing by 2^n the calculated difference, where n is the level of the cup.
Here is another easy solution that simply pours the water into the current glass and then checks if there is extra water then flows to the next level. Here I have used 2D Mat for pouring the water. Then I have converted the 2D mat to 1D having size equals to ith element/glass which we need to return and return that. Implementation wise this is a very easy solution.
private double fillWaterInGlasses(double capacity, double water , int glassToFind) {
int maxLevel = (int)(water/capacity)/2 + 1;
double[][] glasses = new double[maxLevel][maxLevel];
// Pour total water in top glass initially.
glasses[0][0] = water;
int level=0;
boolean waterInLevel = true;
while(waterInLevel) {
waterInLevel = false;
// For each glass in the level.
for(int j=0; j<=level; j++) {
// If the glass has more liquid then it can store then pour it to glasses under it.
if(glasses[level][j] > capacity) {
double extraWater = glasses[level][j] - capacity;
glasses[level][j] = capacity;
glasses[level+1][j] += extraWater / 2;
glasses[level+1][j+1] += extraWater / 2;
waterInLevel = true;
}
}
level++;
}
double res[] = new double[glassToFind];
int k =0;
for (int i = 0; i < glasses.length; i++) {
for (int j = 0; j <= i; j++) {
res[k] = glasses[i][j];
if (k == glassToFind-1){
return res[glassToFind-1];
}
k++;
}
}
return res[glassToFind-1];
}
If you model the pyramid into a graph, the problem converts into a breadth first search. As you traverse each node, get its neighbours and store their overflow quantity. If a neighbour was already retrieved by a previous node (this will happen in the case of 5 node because node 2 and node 3 have an edge to it), you will have to update the overflow based on its capacity and what's already been filled (by node 2; assuming node 2 was traversed before node 3).