Project Euler 18 - C++ Passing Array By Reference - c++

See: http://projecteuler.net/problem=18 for a better explanation of the problem than I could give here.
I like my algorithm; It's not brute-force. I look at all of the 3x3 triangles at the bottom of the pyramid to calculate which route is best, then assign that value back to the row. That's why my function is called ConsolidateBottomThreeRows -- I'll end up calculating the best path from the bottom up, assigning to the top-most of the bottom 3 rows each time, creating a smaller and smaller pyramid until my top row contains the answer.
My issue isn't a question of algorithm, simply syntax. I can't figure out [yes, even with Googling] how to get my arrays passed correctly. Here is my output when I try to compile:
C:\MyApps\Euler>g++ Prob_18.cpp -o Prob_18
Prob_18.cpp: In function 'void ConsolidateBottomThreeRows(int*, int*, int*)':
Prob_18.cpp:17:20: error: request for member 'size' in 'top', which is of non-class type 'int*'
And my code:
#include <iostream>
using namespace std;
int CrunchSmallTriangle(int top[], int middle[], int bottom[])
{
int biggest=0;
if(middle[0]+bottom[0] > biggest) biggest=middle[0]+bottom[0];
if(middle[0]+bottom[1] > biggest) biggest=middle[0]+bottom[1];
if(middle[1]+bottom[0] > biggest) biggest=middle[1]+bottom[0];
if(middle[1]+bottom[1] > biggest) biggest=middle[1]+bottom[1];
return biggest+top[0];
}
void ConsolidateBottomThreeRows(int top[], int middle[], int bottom[])
{
int SmallTop[0], SmallMiddle[2], SmallBottom[3];
for(int x=0;x<top.size();x++)
{
SmallTop[0]=top[x];
SmallMiddle[0]=middle[x];
SmallMiddle[1]=middle[x+1];
SmallBottom[0]=bottom[x];
SmallBottom[1]=bottom[x+1];
SmallBottom[2]=bottom[x+2];
top[x]=CrunchSmallTriangle(SmallTop, SmallMiddle, SmallBottom);
}
}
int main()
{
int row1[1]={75};
int row2[2]={95,64};
int row3[3]={17,47,82};
int row4[4]={18,35,87,10};
int row5[5]={20,4,82,47,65};
int row6[6]={19,1,23,75,3,34};
int row7[7]={88,2,77,73,7,63,67};
int row8[8]={99,65,4,28,6,16,70,92};
int row9[9]={41,41,26,56,83,40,80,70,33};
int row10[10]={41,48,72,33,47,32,37,16,94,29};
int row11[11]={53,71,44,65,25,43,91,52,97,51,14};
int row12[12]={70,11,33,28,77,73,17,78,39,68,17,57};
int row13[13]={91,71,52,38,17,14,91,43,58,50,27,29,48};
int row14[14]={63,66,4,68,89,53,67,30,73,16,69,87,40,31};
int row15[15]={4,62,98,27,23,9,70,98,73,93,38,53,60,4,23};
ConsolidateBottomThreeRows(row13, row14, row15);
ConsolidateBottomThreeRows(row11, row12, row13);
ConsolidateBottomThreeRows(row9, row10, row11);
ConsolidateBottomThreeRows(row7, row8, row9);
ConsolidateBottomThreeRows(row5, row6, row7);
ConsolidateBottomThreeRows(row3, row4, row5);
ConsolidateBottomThreeRows(row1, row2, row3);
cout<<row1[0];
}
Any help is greatly appreciated. Thanks!

Arrays do not have a size() method! You will either need to pass the size to the function as a separate parameter, or use a container (such as std::vector).

Firstly, I agree with the other answers that switching from raw C-arrays to std::array<> or std::vector<> would be the best move. However, that being said, as chris commented, because your C-arrays are statically sized, making ConsolidateBottomThreeRows into a function template makes the following possible as well:
template<std::size_t TopN, std::size_t MidN, std::size_t BotN>
void ConsolidateBottomThreeRows(int (&top)[TopN],
int (&middle)[MidN],
int (&bottom)[BotN])
{
int SmallTop[1], SmallMiddle[2], SmallBottom[3];
for (std::size_t x = 0; x != TopN; ++x)
{
SmallTop[0] = top[x];
SmallMiddle[0] = middle[x];
SmallMiddle[1] = middle[x + 1];
SmallBottom[0] = bottom[x];
SmallBottom[1] = bottom[x + 1];
SmallBottom[2] = bottom[x + 2];
top[x] = CrunchSmallTriangle(SmallTop, SmallMiddle, SmallBottom);
}
}
Note that your declaration of SmallTop has the wrong dimensions.

You need to replace top.size() with a size passed in to the function. Better yet, don't use arrays and use vectors instead. Then your top.size() will work.

Related

Understanding the dp on tree

I was recently solving a problem from Codeforces. After giving it a lot of tries I was not able to get how in tree dp the matrix calculation works in the editorial solution. The following is the code where I have added comments to the parts I don't understand in it.
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int f[2][10010][110];//0 max 1 min
char s[10010];
int tr[10010][2],size,n,fa[10010],p,m,minn,pre;
void dfs(int x)
{
//cout<<x<<" "<<f[0][x][0]<<endl;
if (!tr[x][0]) return;
int l=tr[x][0],r=tr[x][1];
dfs(l),dfs(r);
/*The part which gets complicated need help why and how this calculation works*/
for (int i=0;i<=minn;i++)
for (int j=0;i+j<=minn;j++)
{
f[0][x][i+j+(p<m)]=max(f[0][x][i+j+(p<m)],f[0][l][i]+f[0][r][j]);
f[0][x][i+j+(p>=m)]=max(f[0][x][i+j+(p>=m)],f[0][l][i]-f[1][r][j]);
f[1][x][i+j+(p<m)]=min(f[1][x][i+j+(p<m)],f[1][l][i]+f[1][r][j]);
f[1][x][i+j+(p>=m)]=min(f[1][x][i+j+(p>=m)],f[1][l][i]-f[0][r][j]);
}
}
int main()
{
scanf("%s",s+1);
scanf("%d%d",&p,&m);
memset(f[0],-63,sizeof(f[0]));
memset(f[1],63,sizeof(f[1]));
/* Why we have used min of the two and how does it handle both condition */
minn=min(p,m);
n=strlen(s+1);
size=1;pre=size;
for (int i=1;i<=n;i++)
{
if (s[i]=='('||s[i]=='?')
{
tr[pre][tr[pre][0]?1:0]=++size;
fa[size]=pre;
pre=size;
}
else if (s[i]==')') pre=fa[pre];
else f[0][size][0]=f[1][size][0]=s[i]-'0',pre=fa[pre];
}
dfs(1);
printf("%d",f[0][1][minn]);
}
The part where I get lost is this
f[0][x][i+j+(p<m)]=max(f[0][x][i+j+(p<m)],f[0][l][i]+f[0][r][j]);
f[0][x][i+j+(p>=m)]=max(f[0][x][i+j+(p>=m)],f[0][l][i]-f[1][r][j]);
f[1][x][i+j+(p<m)]=min(f[1][x][i+j+(p<m)],f[1][l][i]+f[1][r][j]);
f[1][x][i+j+(p>=m)]=min(f[1][x][i+j+(p>=m)],f[1][l][i]-f[0][r][j]);
I always struggle with such types of problems. Can someone give the link to approach such problems.
Which part of the lines don't you understood? I take one line
f[0][x][i+j+(p<m)]=max(f[0][x][i+j+(p<m)],f[0][l][i]+f[0][r][j]);
and rewrite it
const int index_max = 0;
int y = i+j + (p<m? 1: 0); // in your code p<m is cast to int, true=1, false=0
int old_max = f[index_max][x][y];
int next_value = f[index_max][l][i] + f[index_max][r][j]:
f[index_max][x][y] = max(old_max, next_value);
You are looking for the maximum of the next_values of your double-loop. As l, r are fixed the next_values are sums of values in two rows.
Similar for the other 3 lines.

creating a vector of objects

I have just coded while on the train this, that creates a vector of objects.
I would appreciate if someone has a suggestion to make it more elegant or effective?
#include <iostream>
#include <vector>
using namespace std;
typedef struct{
short xpos,ypos;
short width, height;
short area;
}LABELPROP;
int get_property(int n,LABELPROP *pP)
{
if(n%2)
{
pP->xpos=n*2;
pP->ypos=n*3;
return 1;
}
else return 0;
}
int main()
{
vector<LABELPROP> myvector;
cout<<"Initial Number :"<<myvector.size()<<endl;
LABELPROP temporal;
// LABELPROP *pT=&temporal;
for(int n=1;n<=10;n++) //10 objects
{
//if(get_property(n,pT))
if(get_property(n,&temporal))
myvector.push_back(temporal);
}
for(int i=0;i<myvector.size();i++)
cout<<"("<<myvector[i].xpos<<","<<myvector[i].ypos<<")"<<endl;
return 0;
}
As you can see I eliminated an unnecessary pointer that I originally put.
the temporal struct gets its values from the get_property function so that is why I put that as a pointer
Thanks in advance
You are writing C++ in a C style. You don't need to typedef your struct, just use:
struct LabelProp
{
LabelProp(short xpos, short ypos) : xpos(xpos), ypos(ypos) {}
short xpos,ypos;
short width, height;
short area;
};
I also changed the naming, because most conventions use all uppercase names as constants or macros. I also added a constructor to be used.
You have get_property returning an int, but since this is c++, return a bool.
Probably an even better idea would be replace get_property with addIfOdd and have it look something like:
void addIfOdd(int n, std::vector<LabelProp>& results)
{
if(n%2)
{
results.emplace_back(n * 2, n * 3);
}
}
int main()
{
std::vector<LabelProp> myvector;
for(int n=1;n<=10;n++) //10 objects
{
addIfOdd(n, myvector);
}
}
It looks like you are unnecessarily iterating over a full range of integers when it is in fact the odd numbers that you're after. Why not limit the range to odd numbers? (only half as many iterations), e.g.:
for(int i = 1; i < 10; i += 2) { /* 1, 3, ..., 9 */ }
Using modern C++ you could add a constructor to your class:
explicit label_prop(int n)
: xpos{static_cast<short>(n * 2)}, ypos{static_cast<short>(n * 3)} {}
and replace all your code with a std::generate_n, e.g.:
std::generate_n(std::back_inserter(myvector), 5, [n = 1] () mutable {
return label_prop{std::exchange(n, n + 2)};
});
Here, you're specifying that you want 5 consecutive odd numbers, starting at 1.
If your compiler is modern enough and supports C++11 features, you could use the new range-based for loop, like this:
for(auto& x : myvector)
cout<<"("<<x.xpos<<","<<x.ypos<<")"<<endl;
Also, in order to improve the eficiency you can use the reserve method if you know in advance the size of the vector, so that you can avoid unnecessary object reallocations and memory allocations/deallocations, which can be expensive.

Some questions regarding 2d Vectors, C++

This 2d vector is being used to hold a game-board for minesweeper. I want to create a 2d vector of struct cell, which has several "state" variables all holding information needed to construct the game board (I am creating a basic minesweeper game to run on the command line, very rudimentary, just want to get a better grasp of classes). First of all, what am I doing wrong when trying to pass the vector to the void function? And then how would I be able to access the separate variables to read and write to them? I know this may be unusual (could solve using arrays) but I'd like to do it a little differently. I have looked through various forums but people don't seem to use this approach. Thanks guys.
EDIT:
What I'm trying to accomplish with the vector of cell's is basically 3 vectors in 1 so that I can simultaneously use the information in the different states to check whether various conditions have been met when a player makes a move (i.e. check whether there is a mine there, or whether that spot has already been opened/marked/unmarked etc.) Please let me know if the code below doesn't allow for what I want to accomplish.
code:
#include <iostream>
#include <vector>
using namespace std;
void gameboard(vector<vector<int>> &stateboard)
struct cell
{
int state; //( 0 hidden, 1 revealed, 2 marked)
int value; //(-1 mine, 0 no surrounding, # > 0
bool isMine;
};
void gameboard(vector<vector<int>> &stateboard)
{
}
int main()
{
int columns = 10;
int rows = 10;
vector <vector<cell> > gameboard(rows, vector<cell>(columns));
gameboard(&gameboard);
return 0;
}
sorry guys, this piece of code doesn't even begin to resemble the outline I have in Xcode, I was just trying to present the question in an easier to follow manner and threw this together.
new code:
#include <iostream>
#include <vector>
using namespace std;
struct cell
{
int state; //( 0 hidden, 1 revealed, 2 marked)
int value; //(-1 mine, 0 no surrounding, # > 0
bool isMine;
};
void game_state(vector<vector<cell>> &stateboard)
{
}
int main()
{
int columns = 10;
int rows = 10;
vector <vector<cell> > gameboard(rows, vector<cell>(columns));
game_state(gameboard);
return 0;
}
I guess having the same name for a function and vector was throwing Xcode off, which is why I made game board a reference originally but now I see why that was stupid. Now that this works, how can i specifically read and write to just the bool isMine variable? I'm not asking for you to do it completely but a basic line of code showing me how to access that specific part would be a greatly help me. Am I conceptualizing this incorrectly?
hope it helps you:
#include <iostream>
#include <vector>
// your columns and rows are equal,
//and they should no change, so i think better to do them const
const int BOARD_SIZE = 10;
struct cell {
int state;
int value;
bool isMine;
};
void game_state(std::vector < std::vector <cell > > &stateboard) {
}
int main (){
std::vector < std::vector <cell > > gameboard;
//I give more preference to initialize matrix like this
gameboard.resize(BOARD_SIZE);
for (int x = 0; x < BOARD_SIZE; x++) {
gameboard[x].resize(BOARD_SIZE);
for (int y = 0; y < BOARD_SIZE; y++) {
// and this is an example how to use bool is mine
// here all cells of 10x10 matrix is false
// if you want place mine in a first cell just change it
// to gameboard[0][0].isMine = true;
gameboard[x][y].isMine = false;
}
}
game_state(gameboard);
return 0;
}

Variable size array in C++

This is interesting. I want to make a 2D array where one of the dimensions is a variable. Here is my code:
int main(void) {
const int rows = numlines("health.txt");
float data[rows][5] = {0};
readIntoArray(data, 5, rows, "health.txt");
return 0;
}
Line 3 "rows" is underlined with an error. It says: "Expression must have a constant value." Apparently it works for other people to use const in these situations. But mine might work differently because my variable is defined by a function. Here is that function:
int numlines(string filename) {
int number_of_lines = 0;
ifstream fin(filename);
string line;
while (getline(fin, line)) {
++number_of_lines;
}
return number_of_lines;
}
I have tried following other suggestions and making my code follow this format:
(Replace lines 2 & 3 of the first code block with this.)
int rows = numlines("health.txt");
float **data;
data = new float*[rows]; //The height is defined by the function
for (int i = 0; i < rows; i++) {
data[i] = new float[5]; //The width is 5
}
But then that causes an error on "data" in line 4 of the first codeblock. The error is Argument of type "float**" is incompatible with parameter of type "float (*)[5]". Here is the first line of the relevant function:
void readIntoArray(float data[][MAXCOLUMNS], int arrayX, int arrayY, string filename)
MAXCOLUMNS is #defined as 5.
How do I pass the 2D array into the function without creating an error?
I am not the most experienced in c++, so I might be missing something obvious.
There is a difference between:
const int rows = numlines("health.txt");
and
const int rows = 20;
In both cases the value of the variable cannot modified once it is initialzed. The difference is that in the first case, the value won't be known until run time while in the second case, the value is known at compile time.
In C++, an array can be declared using a variable only if its value is known at compile time.
That explains why you cannot use:
const int rows = numlines("health.txt");
float data[rows][5] = {0};
but you can use:
const int rows = 20;
float data[rows][5] = {0};
You can easily get around that issue by using an std::vector of std::vectors.
const int rows = numlines("health.txt");
std::vector<std::vector<float>> data(rows, std::vector<float>(5));
Since you know the size of the inner array, you can also you std::array. It will make the declaration a little simpler.
std::vector<std::array<float, 5>> data(rows);
In C++,you can use "std::vector< T >" to save your data as a variable size array.
Just learn how to use STL,it would simplify your works.
You can use alloca to implement the equivalent of a variable length array:
float (*data)[5] = (float (*)[5]) _alloca(rows * 5 * sizeof(float));
This will allocate local (stack) space and set data to point to the first row (with 5 columns) of a matrix of floats. The 5 can be replaced with a constant. You can then use data like a normal local matrix, data[ i ][ j ] ... . Depending on the compiler, the name may be alloca() instead of _alloca(), and the cast (float (*)[5]) may not be needed.
Since this is a local allocation, it's automatically freed when the function exits.

Two point crossover operation

I've been trying to write a code for two point crossover operation in a genetic algorithm. At first two random gene location is selected. After that, two chromosomes swap their genes which are located btw random numbers called genelocation1 and genelocatıon2.
for example First Gene [0.3,0.2,0.4,0,0.1,0.5,0.7]
Second Gene [0.25,0.6,0.45,0.15,0.80,0.9,0.85]
rndm genelocation1=3
rdnm gnelocation2 =5
child Gene1 [0.3,0.2,0.4,0.15,0.80,0.5,0.7]
Gene2 [0.25, 0.6, 0.45, 0, 0.1,0.9,0.85]
my problem is this: since two numbers are generated randomly, i could not define an array like array[genelocation2-genelocation1].. How can i solve the problem. here is my whole code about two point crossover. pointers maybe a solution but i am not good at pointers.
Here is the code:
void Xover (int mother,int father)
{
int tempo;
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
tempo=Rndmgenelocation1;
Rndmgenelocation1=Rndmgenelocation2;
Rndmgenelocation2=tempo;
}
int size=(Rndmgenelocation2-Rndmgenelocation1);
int Temp1[size];//this makes an error
int ppp=Rndmgenelocation1;
for (int pp=Rndmgenelocation1;pp<Rndmgenelocation2;pp++)
{
Temp1[pp]=Sol_list[father].Chromosome[ppp];
ppp++;
}
int pppx=Rndmgenelocation1;
for (int ppx=Rndmgenelocation1;ppx<Rndmgenelocation2;ppx++)
{
Sol_list[father].Chromosome[ppx]=Sol_list[mother].Chromosome[pppx];
pppx++;
}
int ppplx=Rndmgenelocation1;
for (int pplx=Rndmgenelocation1;pplx<Rndmgenelocation2;pplx++)
{
Sol_list[father].Chromosome[pplx]=Temp1[ppplx];
ppplx++;
}
return;
}
You can't define an array of variable size on the stack.
You could use
int *Temp1=new int[size]
You then must not forget to call
delete[] Temp1;
at the end of your function!
edit:
I didn't test my code below, but the following should do what you want in a more efficient (and more understandable) way:
#include <algorithm>
void Xover (int mother,int father)
{
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
std::swap(Rndmgenelocation1,Rndmgenelocation2);
}
for (int pp=Rndmgenelocation1;pp<Rndmgenelocation2;pp++)
{
std::swap(Sol_list[father].Chromosome[pp],Sol_list[mother].Chromosome[pp]);
}
return;
}
edit2:
I just found here another even better way - the STL implements a ready-to-use cross over algorithm. Use:
#include <algorithm>
void Xover (int mother,int father)
{
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
std::swap(Rndmgenelocation1,Rndmgenelocation2);
}
std::swap_ranges(
Sol_list[father].Chromosome[Rndmgenelocation1],
Sol_list[father].Chromosome[Rndmgenelocation2],
Sol_list[mother].Chromosome[Rndmgenelocation1]
);
return;
}
I'm guessing you must not be using g++ as your compiler. If so, you can use a std::vector rather than an array. Simply do
std::vector<int> array(size);
Now you can treat it like a "normal" array though the operator[] syntax. There's also no concern about memory leaks from forgetting to call delete on a pointer.