I'm writing a program that counts all binary trees with n nodes and height k. Every node has 0 or 2 children. The program works but I wanted to add some memoization because the answer is always the same for some particular n and k.
I could create an multidimensional array of pairs but I already have my useful struct now. How could I declare and use this mem variable. I didn't find a good answer on this. I understand pointers but I would prefer a method without memory management.
This is an exercise from the USACO training program btw.
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;
struct state {
int down, half;
state(int d, int h) : down(d), half(h) {}
int valid() {
return down != -1 && half != -1;
}
};
state mem[200][100];
state cnt(int n, int k)
{
if (mem[n][k].valid())
return mem[n][k];
if (n == 1)
return state(k == 1, k != 1);
if (n > pow(2, k) - 1)
return state(-1, -1);
state total(0, 0);
for (int i = 1; i < n - 1; ++i) {
state left = cnt(i, k - 1);
state right = cnt(n - i - 1, k - 1);
if (left.valid() && right.valid()) {
total.down += left.down * right.down +
left.down * right.half +
left.half * right.down;
total.half += left.half * right.half;
}
}
return mem[n][k] = state(total.down % 9901, total.half % 9901);
}
int main()
{
ofstream fout ("nocows.out");
ifstream fin ("nocows.in");
int n, k;
fin >> n >> k;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= k; ++j)
mem[i][j] = state(-1, -1);
cout << cnt(n, k).down << endl;
return 0;
}
You can use a vector of vectors:
std::vector<std::vector<state> > mem;
You can dynamically add to it and needn't worry about size (although if you roughly know the size, you can pre-allocate it to avoid resizing), and also memory clean-up is automatic - when the vector goes out of scope, its components will also be deleted.
Your code doesn't work because you don't have a default constructor for state.
The thing is, when you write state mem[200][100]; the compiler will try to create 100*200 state objects, but it can't. To make this work, you'd need a default constructor in state:
struct state {
state() : down(0), half(0) {} //default constructor
int down, half;
state(int d, int h) : down(d), half(h) {}
int valid() {
return down != -1 && half != -1;
}
};
Related
In my learning process about C++, I am having hard time understanding the best practice for initializing a vector in this particular setting:
struct Data {
vector<int> vec;
Data(const int k) {
for (int i = 0; i < k; i++) vec.push_back(0);
}
};
so that in the main function I just declare Data mydata(10); and mydata will have an attribute vector mydata.vec of k=10 elements.
However, I really find unefficient that one has to setup a for loop and fill mydata.vec element by element, so looking online I found other methods that honestly I did not understand. Anyway, I tried to replace that given constructor with
Data(const int k) {
vec.resize(k);
}
and
Data(const int k) : vec(k,0) {}
with either error messages about modified objects already deleted or segmentation faults happening during runtime. Would you please explain the most C++'ish or most efficient way to initialise a vector in such framework? Consider that for some reason I have little to no understanding about what an initialization list is.
EDIT:
Here I propose a minimal example directly taken from my code:
#include <iostream>
#include <vector>
using namespace std;
struct Data {
vector<unsigned int> mydata, count;
Data(const unsigned int k) {
for (unsigned int i = 0; i < 2 * k; i++) {
mydata.push_back(i + 1);
//count.push_back(0); // HERE 1
}
count.resize(k); // HERE 2
}
void update_stats(const unsigned int p) { count[mydata[0] - 1] += 1; }
};
void twist(Data& stuff, const unsigned int m) {
unsigned int temp;
for (unsigned int i = m; i < 2 * m; i++) {
temp = stuff.mydata[i];
stuff.mydata.erase(stuff.mydata.begin() + i);
stuff.mydata.insert(stuff.mydata.begin() + (i - m) * 2, temp);
}
stuff.update_stats(m);
}
int main() {
unsigned int p, k = 200;
Data stuff(k);
for (p = 1; p <= k; p++) twist(stuff, p);
for (p = k; p >= 1; p--) twist(stuff, p);
cout << stuff.count[stuff.mydata[0] - 1] << endl;
return 0;
}
I am sorry to not to do better in reducing further. This code produces a segmentation fault. However, commenting the HERE 2 line and using HERE 1 apparently saves the situation. I don't get why.
After the for loop
for (unsigned int i = 0; i < 2 * k; i++)
count.push_back(0);
the count vector will contain 2k zeros. But after count.resize(k) it will contain only k zeros.
Fixed constructor might look like this:
Data(const unsigned int k) : mydata(2 * k), count(2 * k, 0) {
std::iota(mydata.begin(), mydata.end(), 1u);
}
To assign sequentially increasing sequence to mydata, you can use std::iota algorithm. , 0 can be dropped without changing the behaviour, but you might want to be explicit about the initial value.
Semantics of these two constructors is very simple:
explicit vector(size_type count);
Constructs the container with count default-inserted instances of T. No copies are made.
vector(size_type count, const T& value);
Constructs the container with count copies of elements with value value.
For T = unsigned int, the "default-inserted instance" is just 0u.
#include <iostream>
using namespace std;
int main() {
int n,d,i=0,temp;
cin>>n>>d;
int a[1000000];
for(i=0;i<n;i++){
cin>>a[i];
}
while(d--){
temp=a[0];
for(i=1;i<n;i++){
a[i-1]=a[i];}
a[n-1]=temp;
}
for(i=0;i<n;i++){
cout<<a[i]<<" ";
}
return 0;
}
how to optimize it further as it's giving TLE error. the input file is very large obviously.
Some suggestions:
Rotate by the full amount d in a single loop (note that the result is a different array b):
for (i = 0; i < n; i++) {
b[(i+n-d) % n]=a[i];
}
Don't touch the array at all but transform the index when accessing it, for example:
cout << a[(i+n-d) % n] << " ";
The second version requires extra calculation to be done whenever accessing an array element but it should be faster if you don't need to access all array elements after each rotate operation.
There is also a way to do the rotation in-place by using a helper function that reverses a range of the array. It's a bit odd but might be the best solution. For convenience I have used a std::vector instead of an array here:
void ReverseVector( std::vector<int>& a, int from, int to ) {
for (auto i = 0; i < (to - from) / 2; i++) {
auto tmp = a[from + i];
a[from + i] = a[to - i];
a[to-i] = tmp;
}
}
void RotateVector( std::vector<int>& a, int distance ) {
distance = (distance + a.size()) % a.size();
ReverseVector( a, 0, a.size() - 1 );
ReverseVector( a, 0, distance - 1 );
ReverseVector( a, distance, a.size() - 1 );
}
So i was trying to make the challage: Breadth First Search: Shortest Reach on HackerRank, but i keep getting the bad alloc exception when the tests have great numbers of node/edges. The program works on the first test, so i don't think, it's something wrong with the implementation.
So here is the implementation:
(sorry for the indentation , my first question)
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <queue>
#include <limits.h>
using namespace std;
int main() {
//test numbers
int t;
//the cost
int cost = 6;
cin >> t;
//for each test
for (int nt = 0; nt < t; ++nt) {
int n, e;
int snode;
queue <int> *que = new queue<int>();
//read the node/edges
cin >> n >> e;
//distance/visited/parents arrays/adjlist vector
int dist[n + 1] = {-1};
bool visited[n + 1] = {false};
int parents[n + 1] = {-1};
vector< vector<int> > adjList(n + 1);
//read into the adjlist, unoriented graph, each edge has 6 weight
for (int ne = 0; ne < e; ++ne) {
int x, y;
cin >> x >> y;
adjList[x].push_back(y);
adjList[y].push_back(x);
}
//read the starting node
cin >> snode;
dist[snode] = 0;
//do actual bfs
que->push(snode);
visited[snode] = true;
while(!que->empty()) {
int c_node = que->front();
que->pop();
for (int i = 0; i < adjList[c_node].size(); ++i) {
if (visited[adjList[c_node].at(i)] == false) {
que->push(adjList[c_node].at(i));
parents[adjList[c_node].at(i)] = c_node;
dist[adjList[c_node].at(i)] = dist[parents[adjList[c_node].at(i)]] + cost;
visited[adjList[c_node].at(i)] == true;
}
}
}
//print at output the distance from the starting node to each other node
//if unreachable, print -1
for (int i = 1; i < n + 1; ++i) {
if (i == snode) {
} else if (dist[i] == 0 && i != snode) {
cout << "-1 ";
} else {
cout << dist[i] << " ";
}
}
cout << "\n";
}
return 0;
}
Am i doing something wrong, i haven't seen anyone else complain on this matter in the discussion section of the site.
How can i avoid the exception to be thrown and from where does it come?
Thank you!
I don't know, exactly, what is the cause of your exception; and I don't know ho to reproduce your problem because depends (I suppose) from the input values. A lot of input values, I suppose.
But I see some weak points (IMHO) of your code, so I try to point your attention to them.
1) you alloc a std::queue in your for cycle
queue <int> *que = new queue<int>();
but you never free it; it's a waste of memory
2) you're using C-style variable-length arrays
int dist[n + 1] = {-1};
bool visited[n + 1] = {false};
int parents[n + 1] = {-1};
They aren't valid C++ standard code. I suggest you the use of standard containers (std::vector or std::queue).
3) you're initializing your C-style variable-length arrays with a initializers lists with only an element (-1 or false). I suppose your intention was initialize all n+1 elements with -1 and false. But this syntax initialize only the first element of the array with -1 and false.
If you want to initialize all n+1 element to -1 and false, the solution is (again) use standard containers; by example
std::vector<int> dist(n+1, -1);
std::vector<bool> visited(n+1, false);
std::vector<int> parents(n+1, -1);
4) you access arrays without bounds checking. By example:
cin >> snode;
dist[snode] = 0;
where snode is a int variable; if you insert a negative value, or a value over n, you write dist out of its bounds, devastating the memory. This, I suppose, can explain your "bad alloc exception".
Suggestion: use standard containers (again) instead of C-style array and use at() (that perform bounds checking) instead []; so
cin >> snode;
dist.at(snode) = 0;
5) sorry for my bad English (ok, I'm joking: this isn't one of your weak points; this is one of mine).
I am trying to make a bubble sort that will shift the position of several parallel arrays based on the string "desc", in ascending order. When I run the program, I just get a blank screen and the program never finishes.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;
const int maxp = 50;
void swap(string& a, string& b)
{
string temp;
temp = a;
a = b;
b = temp;
}
//These swapem functions are the functions used to move the array
//values when the sort function is called
void swap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swap(char& a, char& b)
{
char temp;
temp = a;
a = b;
b = temp;
}
void swap(double& a, double& b)
{
double temp;
temp = a;
a = b;
b = temp;
}
void sort(int id[], string desc[], int numsold[], double price[],
double dolars[], int nump)
{
int i, j;
for (j = 0; j < nump - 1; j++)
for (i = 0; i = nump - 1; i++) //I'm sorting a group of parallel arrays by the
if (desc[i] > desc[i+1])//string "desc", and the others are being moved based off of that
{
swapem(desc[i], desc[i + 1]);
swapem(id[i], id[i + 1]);
swapem(numsold[i], numsold[i + 1]);
swapem(price[i], price[i + 1]);
swapem(dolars[i], dolars[i + 1]);
}
}
int main()
{
int id[maxp], numsold[maxp], nump;
double price[maxp], dolars[maxp];
string desc[maxp];
ifstream inf;
ofstream outf;
inf.open("storesales.dat");
outf.open("storesales.ot");
outf.setf(ios::fixed);
outf.precision(2);
initem(desc, id, numsold, nump, price, dolars);
readem(id,numsold,nump,price,desc);
printem(id, desc, numsold,nump, price, outf);
getsales(numsold, price, dolars,nump);
sortem(id, desc, numsold, price, dolars, nump);
printem(desc, id, numsold, nump, price, dolars, outf);
system("pause");
}
I know that these functions are the problem because I retested them and commented the calls out, the program finished. The problem is, I don't know what is wrong.
Nasted loop in sortem():
for (i = 0; i = nump - 1; i++)
should be condition instead of assignment i.e:
for (i = 0; i < nump - 1; i++)
Second parameter of for loop is expected condition. Assignment i = nump always return true thereby infinite loop. As a result, your blank screen.
the error is in the 2nd loop
for best performance you can have the inner loop as follows:
for (i = j; i < nump - 1; i++)
As this is already answered just few silly suggestions:
Why are you want j to loop nump-1 times?
You should stop when no swap in the nested loop occurs... which mean stop when sorting is done. Otherwise your sort will always take the worst case runtime.
for (j=1;j;) // loop while j is set
for (j=0,i=0;i<nump-1;i++) // reset j
if (desc[i] > desc[i+1]) // if swap needed
{
swapem(desc[i], desc[i + 1]); // swap elements
swapem(id[i], id[i + 1]);
swapem(numsold[i], numsold[i + 1]);
swapem(price[i], price[i + 1]);
swapem(dolars[i], dolars[i + 1]);
j=1; // and set j so this loops until array is ordered
}
Also you are using parallel arrays which have its merits but I think in your case use of struct or class would be far better simplifying the code (especially the swap) a bit having single array with all the info inside.
Given two strings string X of length x1 and string Y of length y1, find the longest sequence of characters that appear left to right (but not necessarily in contiguous block) in both strings.
e.g if X = ABCBDAB and Y = BDCABA, the LCS(X,Y) = {"BCBA","BDAB","BCAB"} and LCSlength is 4.
I used the standard solution for this problem:
if(X[i]=Y[j]) :1+LCS(i+1,j+1)
if(X[i]!=Y[j]) :LCS(i,j+1) or LCS(i+1,j), whichever is greater
and then I used memorization, making it a standard DP problem.
#include<iostream>
#include<string>
using namespace std;
int LCS[1024][1024];
int LCSlen(string &x, int x1, string &y, int y1){
for(int i = 0; i <= x1; i++)
LCS[i][y1] = 0;
for(int j = 0; j <= y1; j++)
LCS[x1][j] = 0;
for(int i = x1 - 1; i >= 0; i--){
for(int j = y1 - 1; j >= 0; j--){
LCS[i][j] = LCS[i+1][j+1];
if(x[i] == y[j])
LCS[i][j]++;
if(LCS[i][j+1] > LCS[i][j])
LCS[i][j] = LCS[i][j+1];
if(LCS[i+1][j] > LCS[i][j])
LCS[i][j] = LCS[i+1][j];
}
}
return LCS[0][0];
}
int main()
{
string x;
string y;
cin >> x >> y;
int x1 = x.length() , y1 = y.length();
int ans = LCSlen( x, x1, y, y1);
cout << ans << endl;
return 0;
}
Running here, this solution I used in SPOJ and I got a time limit exceeded and/or runtime error.
Only 14 user solutions are yet accepted. Is there a smarter trick to decrease the time complexity of this question?
LCS is a classical, well studied computer science problem, and for the case with two sequences it is known that its lower bound is O(n·m).
Furthermore, your algorithm implementation has no obvious efficiency bugs, so it should run close to as fast as possible (although it may be beneficial to use a dynamically sized 2D matrix rather than an oversized one, which takes up 4 MiB of memory, and will require frequent cache invalidation (which is a costly operation, since it causes a transfer from main memory to the processor cache, which is several orders of magnitude slower than cached memory access).
In terms of algorithm, in order to lower the theoretical bound you need to exploit specifics of your input structure: for instance, if you are searching one of the strings repeatedly, it may pay to build a search index which takes some processing time, but will make the actual search much faster. Two classical variants of that are the suffix array and the suffix tree.
If it is known that at least one of your strings is very short (< 64 characters) you can use Myers’ bit vector algorithm, which performs much faster. Unfortunately the algorithm is far from trivial to implement. There exists an implementation in the SeqAn library, but using the library itself has a steep learning curve.
(As a matter of interest, this algorithm finds frequent application in bioinformatics, and has been used during the sequence assembly in the Human Genome Project.)
Although I still didn't get an AC because of time limit exceeded ,I was however able to implement the linear space algorithm.In case anyone wants to see, here is the c++ implementation of the Hirschbirg algorithm.
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
int* compute_help_table(const string & A,const string & B);
string lcs(const string & A, const string & B);
string simple_solution(const string & A, const string & B);
int main(void) {
string A,B;
cin>>A>>B;
cout << lcs(A, B).size() << endl;
return 0;
}
string lcs(const string &A, const string &B) {
int m = A.size();
int n = B.size();
if (m == 0 || n == 0) {
return "";
}
else if(m == 1) {
return simple_solution(A, B);
}
else if(n == 1) {
return simple_solution(B, A);
}
else {
int i = m / 2;
string Asubstr = A.substr(i, m - i);
//reverse(Asubstr.begin(), Asubstr.end());
string Brev = B;
reverse(Brev.begin(), Brev.end());
int* L1 = compute_help_table(A.substr(0, i), B);
int* L2 = compute_help_table(Asubstr, Brev);
int k;
int M = -1;
for(int j = 0; j <= n; j++) {
if(M < L1[j] + L2[n-j]) {
M = L1[j] + L2[n-j];
k = j;
}
}
delete [] L1;
delete [] L2;
return lcs(A.substr(0, i), B.substr(0, k)) + lcs(A.substr(i, m - i), B.substr(k, n - k));
}
}
int* compute_help_table(const string &A, const string &B) {
int m = A.size();
int n = B.size();
int* first = new int[n+1];
int* second = new int[n+1];
for(int i = 0; i <= n; i++) {
second[i] = 0;
}
for(int i = 0; i < m; i++) {
for(int k = 0; k <= n; k++) {
first[k] = second[k];
}
for(int j = 0; j < n; j++) {
if(j == 0) {
if (A[i] == B[j])
second[1] = 1;
}
else {
if(A[i] == B[j]) {
second[j+1] = first[j] + 1;
}
else {
second[j+1] = max(second[j], first[j+1]);
}
}
}
}
delete [] first;
return second;
}
string simple_solution(const string & A, const string & B) {
int i = 0;
for(; i < B.size(); i++) {
if(B.at(i) == A.at(0))
return A;
}
return "";
}
Running here.
If the two strings share a common prefix (e.g. "ABCD" and "ABXY" share "AB") then that will be part of the LCS. Same for common suffixes. So for some pairs of strings you can gain some speed by skipping over the longest common prefix and longest common suffix before starting the DP algorithm; this doesn't change the worst-case bounds, but it changes the best case complexity to linear time and constant space.