Calculate Number of path between two nodes - algorithm

Kingdom Connectivity
It has been a prosperous year for King Charles and he is rapidly expanding his kingdom.A beautiful new kingdom has been recently constructed and in this kingdom there are many cities connected by a number of one-way roads.Two cities may be directly connected by more than one roads, this is to ensure high connectivity.
In this new kingdom King Charles has made one of the cities at his financial capital and one as warfare capital and he wants high connectivity between these two capitals.The connectivity of a pair of cities say city A and city B is defined as the number of different paths from city A to city B. A path may use a road more than once if possible. Two paths are considered different if they do not use exactly the same sequence of roads.
There are N cities numbered 1 to N in the new kingdom and M one-way roads . City 1 is the monetary capital and city N is the warfare capital.
You being one of the best programmers in new kingdom need to answer the connectivity of financial capital and warfare capital ,i.e number of different paths from city 1 to city N.
Input Description:
First line contains two integers N and M.
Then follow M lines ,each having two integers say x and y, 1<=x,y<=N , indicating there is a road from city x to city y.
Output Description:
Print the number of different paths from city 1 to city N modulo 1,000,000,000(10^9).If there are infinitely many different paths print "INFINITE PATHS"(quotes are for clarity).
Sample Input:
5 5
1 2
2 4
2 3
3 4
4 5
Sample Output:
2
Sample Input:
5 5
1 2
4 2
2 3
3 4
4 5
Sample Output:
INFINITE PATHS
Constraints:
2<=N<=10,000(10^4)
1<=M<=1,00,000(10^5)
Two cities may be connected by more than two roads and in that case those roads are to be considered different for counting distinct paths
The algorithm that i use to solve the problem is :
Detect if the node n is reachable from node 1.
Its its not then the required ans is 0
If its reachable then find if there is any cycle in the graph by doing dfs from node 0
If there is a cycle then print INFINITE PATHS
If there is no cycle calculate the required ans using the recurrence
F(n) = 1
F(0) = Sumofall F(x) such that x is adjacent to 0
F(x) = 0 if there is no x adjacent to x
I have implemented the solution as :
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<pair<int, int> > > g;
int seen[10001] = {0};
int colour[10001] = {0};
bool has_cycle(int u) {
colour[u] = 1;
for(int i = 0; i < g[u].size(); i++) {
if(colour[g[u][i].first]==1) return true;
if(!colour[g[u][i].first])
if(has_cycle(g[u][i].first)) return true;
}
colour[u] = 2;
return false;
}
bool reachable(int u, int v) {
if(u==v) return true;
seen[u] = true;
for(int i = 0; i < g[u].size(); i++) {
if(!seen[g[u][i].first]) {
if(reachable(g[u][i].first, v)) return true;
}
}
return false;
}
long long mm[10001] = {0};
long long solve(int u, int n) {
if(u==n) return mm[u]=1;
if(mm[u]!=-2) return mm[u];
long long ans = 0;
for(int i = 0; i < g[u].size(); i++) {
ans += ((g[u][i].second%1000000000)*(solve(g[u][i].first, n)%1000000000)%1000000000);
ans %= 1000000000;
}
return mm[u]=ans;
}
long edge[100001];
int main() {
int n, m;
scanf("%d%d", &n, &m);
g.resize(n);
for(int i = 0; i < m; i++) {
int x, y;
scanf("%d%d", &x, &y);
x--; y--;
edge[i] = x*100000+y;
}
sort(edge, edge+m);
edge[m] = -1;
int last = edge[0];
int cnt = 1;
for(int i = 1; i <= m; i++) {
if(edge[i]!=last || i==m) {
int u, v;
u = last/100000;
v = last%100000;
if(i!=0) {
g[u].push_back(make_pair(v, cnt));
}
cnt = 1;
} else {
cnt++;
}
last = edge[i];
}
for(int i = 0; i < n; i++) mm[i] = -2;
if(reachable(0, n-1)) {
if(has_cycle(0)) printf("INFINITE PATHS\n");
else
printf("%lld\n", solve(0, n-1)%1000000000);
} else printf("0\n");
}
I am not able to detect the problem with this algorithm

Number (3)+(4) are wrong:
If its reachable then find if there is any cycle in the graph by
doing dfs from node 0.
If there is a cycle then print INFINITE PATHS
There could be a cycle in the graph, but the required #paths would still be a finite number, if the target is not reachable from the cycle.
Example: Looking for #paths from A to C
A-->D<-->B
|
----->C
In here: G=(V,E), V = {A,B,C,D} and E = {(D,B),(B,D),(A,C),(A,D)}
Though there is a cycle reachable from A (A->D->B->D), there is only one path from A to C.
To find if there are cycles in a path leading from source to target one can create a new graph G'=(V',E'), where V'= { v | there is a path in the original graph from v to target }, and E' = V' x V' [intersection] E (E reduced only to the vertices of V'), and run DFS/BFS on G'.
Also note, that if there are no cycles in G' - it is a DAG by definition, so working on G' from now on, will probably simplify also finding the #paths. (You will also have to trim vertices that are not reachable from source to make sure it is indeed a DAG).

Obvious mistake. Suppose that there is a cycle, but there is no path from the cycle to the second city. Then you will say that there are an infinite number of paths, but the number of paths may actually be finite.

You can reference my code
#include <iostream>
#include <vector>
#include <set>
#include <stack>
#include <map>
#include <algorithm>
#include <iomanip>
#include <numeric>
#include "string.h"
#define MODE 1000000000
using namespace std;
int main () {
vector<int> adj[10001], inv_adj[10001];
int indegree[10001];
int visited[10001];
int ranks[10001];
long long total[10001];
int N, M;
cin >> N >> M;
memset(indegree, 0, (N+1)*sizeof(int));
adj[0].push_back(1);
inv_adj[1].push_back(0);
indegree[1] = 1;
for (int i=0;i<M;i++)
{
int s, t;
cin >> s >> t;
adj[s].push_back(t);
inv_adj[t].push_back(s);
indegree[t]++;
}
stack<int> st;
st.push(0);
memset(visited, 0, (N+1)*sizeof(int));
visited[0] = 1;
while (!st.empty()) {
int v = st.top();
st.pop();
for (int i=0;i<adj[v].size();i++)
if (!visited[adj[v][i]])
{
st.push(adj[v][i]);
visited[adj[v][i]] = 1;
}
}
if(!visited[N])
{
cout << 0 << endl;
return 0;
}
for (int i=1;i<=N;i++)
{
if(!visited[i]){
for (int j=0;j<adj[i].size();j++)
indegree[adj[i][j]]--;
}
}
int count = 0;
stack<int> topo;
for (int i=0;i<=N;i++)
{
int j;
for (j=0;j<=N;j++)
if (visited[j] && indegree[j] ==0)
break;
if (j > N)
{
cout << "INFINITE PATHS" << endl;
return 0;
}
else
{
topo.push(j);
ranks[count++] = j;
if (j == N)
break;
indegree[j] = -1;
for (int k=0;k<adj[j].size();k++)
indegree[adj[j][k]]--;
}
}
memset(total, 0, (N+1)*sizeof(long long));
total[N] = 1;
for (int i=count - 1;i>=0;i--)
{
int r = ranks[i];
for (int j=0;j<inv_adj[r].size();j++)
if(visited[inv_adj[r][j]])
{
total[inv_adj[r][j]] = (total[inv_adj[r][j]] + total[r]) % MODE;
}
}
cout << total[0] << endl;
return 0;
}

Related

Smallest Multiple of given number With digits only 0 and 1

You are given an integer N. You have to find smallest multiple of N which consists of digits 0 and 1 only. Since this multiple could be large, return it in form of a string.
Returned string should not contain leading zeroes.
For example,
For N = 55, 110 is smallest multiple consisting of digits 0 and 1.
For N = 2, 10 is the answer.
I saw several related problems, but I could not find the problem with my code.
Here is my code giving TLE on some cases even after using map instead of set.
#define ll long long
int getMod(string s, int A)
{
int res=0;
for(int i=0;i<s.length();i++)
{
res=res*10+(s[i]-'0');
res%=A;
}
return res;
}
string Solution::multiple(int A) {
if(A<=1)
return to_string(A);
queue<string>q;
q.push("1");
set<int>st;
string s="1";
while(!q.empty())
{
s=q.front();
q.pop();
int mod=getMod(s,A);
if(mod==0)
{
return s;
}
else if(st.find(mod)==st.end())
{
st.insert(mod);
q.push(s+"0");
q.push(s+"1");
}
}
}
Here is an implementation in Raku.
my $n = 55;
(1 .. Inf).map( *.base(2) ).first( * %% $n );
(1 .. Inf) is a lazy list from one to infinity. The "whatever star" * establishes a closure and stands for the current element in the map.
base is a method of Rakus Num type which returns a string representation of a given number in the wanted base, here a binary string.
first returns the current element when the "whatever star" closure holds true for it.
The %% is the divisible by operator, it implicitly casts its left side to Int.
Oh, and to top it off. It's easy to parallelize this, so your code can use multiple cpu cores:
(1 .. Inf).race( :batch(1000), :degree(4) ).map( *.base(2) ).first( * %% $n );
As mentioned in the "math" reference, the result is related to the congruence of the power of 10 modulo A.
If
n = sum_i a[i] 10^i
then
n modulo A = sum_i a[i] b[i]
Where the a[i] are equal to 0 or 1, and the b[i] = (10^i) modulo A
Then the problem is to find the minimum a[i] sequence, such that the sum is equal to 0 modulo A.
From a graph a point of view, we have to find the shortest path to zero modulo A.
A BFS is generally well adapted to find such a path. The issue is the possible exponential increase of the number of nodes to visit. Here, were are sure to get a number of nodes less than A, by rejecting the nodes, the sum of which (modulo A) has already been obtained (see vector used in the program). Note that this rejection is needed in order to get the minimum number at the end.
Here is a program in C++. The solution being quite simple, it should be easy to understand even by those no familiar with C++.
#include <iostream>
#include <string>
#include <vector>
struct node {
int sum = 0;
std::string s;
};
std::string multiple (int A) {
std::vector<std::vector<node>> nodes (2);
std::vector<bool> used (A, false);
int range = 0;
int ten = 10 % A;
int pow_ten = 1;
if (A == 0) return "0";
if (A == 1) return "1";
nodes[range].push_back (node{0, "0"});
nodes[range].push_back (node{1, "1"});
used[1] = true;
while (1) {
int range_new = (range + 1) % 2;
nodes[range_new].resize(0);
pow_ten = (pow_ten * ten) % A;
for (node &x: nodes[range]) {
node y = x;
y.s = "0" + y.s;
nodes[range_new].push_back(y);
y = x;
y.sum = (y.sum + pow_ten) % A;
if (used[y.sum]) continue;
used[y.sum] = true;
y.s = "1" + y.s;
if (y.sum == 0) return y.s;
nodes[range_new].push_back(y);
}
range = range_new;
}
}
int main() {
std::cout << "input number: ";
int n;
std::cin >> n;
std::cout << "Result = " << multiple(n) << "\n";
return 0;
}
EDIT
The above program is using a kind of memoization in order to speed up the process but for large inputs memory becomes too large.
As indicated in a comment for example, it cannot handle the case N = 60000007.
I improved the speed and the range a little bit with the following modifications:
A function (reduction) was created to simplify the search when the input number is divisible by 2 or 5
For the memorization of the nodes (nodes array), only one array is used now instead of two
A kind of meet-in-the middle procedure is used: in a first step, a function mem_gen memorizes all relevant 01 sequences up to N_DIGIT_MEM (=20) digits. Then the main procedure multiple2 generates valid 01 sequences "after the 20 first digits" and then in the memory looks for a "complementary sequence" such that the concatenation of both is a valid sequence
With this new program the case N = 60000007 provides the good result (100101000001001010011110111, 27 digits) in about 600ms on my PC.
EDIT 2
Instead of limiting the number of digits for the memorization in the first step, I now use a threshold on the size of the memory, as this size does not depent only on the number of digits but also of the input number. Note that the optimal value of this threshold would depend of the input number. Here, I selected a thresholf of 50k as a compromise. With a threshold of 20k, for 60000007, I obtain the good result in 36 ms. Besides, with a threshold of 100k, the worst case 99999999 is solved in 5s.
I made different tests with values less than 10^9. In about all tested cases, the result is provided in less that 1s. However, I met a corner case N=99999999, for which the result consists in 72 consecutive "1". In this particular case, the program takes about 6.7s. For 60000007, the good result is obtained in 69ms.
Here is the new program:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <chrono>
#include <cmath>
#include <algorithm>
std::string reverse (std::string s) {
std::string res {s.rbegin(), s.rend()};
return res;
}
struct node {
int sum = 0;
std::string s;
node (int sum_ = 0, std::string s_ = ""): sum(sum_), s(s_) {};
};
// This function simplifies the search when the input number is divisible by 2 or 5
node reduction (int &X, long long &pow_ten) {
node init {0, ""};
while (1) {
int digit = X % 10;
if (digit == 1 || digit == 3 || digit == 7 || digit == 9) break;
switch (digit) {
case(0):
X /= 10;
break;
case(2):
case(4):
case(6):
case(8):
X = (5*X)/10;
break;
case(5):
X = (2*X)/10;
break;
}
init.s.push_back('0');
pow_ten = (pow_ten * 10) % X;
}
return init;
}
const int N_DIGIT_MEM = 30; // 20
const int threshold_size_mem = 50000;
// This function memorizes all relevant 01 sequences up to N_DIGIT_MEM digits
bool gene_mem (int X, long long &pow_ten, int index_max, std::map<int, std::string> &mem, node &result) {
std::vector<node> nodes;
std::vector<bool> used (X, false);
bool start = true;
for (int index = 0; index < index_max; ++index){
if (start) {
node x = {int(pow_ten), "1"};
nodes.push_back (x);
} else {
for (node &x: nodes) {
x.s.push_back('0');
}
int n = nodes.size();
for (int i = 0; i < n; ++i) {
node y = nodes[i];
y.sum = (y.sum + pow_ten) % X;
y.s.back() = '1';
if (used[y.sum]) continue;
used[y.sum] = true;
if (y.sum == 0) {
result = y;
return true;
}
nodes.push_back(y);
}
}
pow_ten = (10 * pow_ten) % X;
start = false;
int n_mem = nodes.size();
if (n_mem > threshold_size_mem) {
break;
}
}
for (auto &x: nodes) {
mem[x.sum] = x.s;
}
//std::cout << "size mem = " << mem.size() << "\n";
return false;
}
// This function generates valid 01 sequences "after the 20 first digits" and then in the memory
// looks for a "complementary sequence" such that the concatenation of both is a valid sequence
std::string multiple2 (int A) {
std::vector<node> nodes;
std::map<int, std::string> mem;
int ten = 10 % A;
long long pow_ten = 1;
int digit;
if (A == 0) return "0";
int X = A;
node init = reduction (X, pow_ten);
if (X != A) ten = ten % X;
if (X == 1) {
init.s.push_back('1');
return reverse(init.s);
}
std::vector<bool> used (X, false);
node result;
int index_max = N_DIGIT_MEM;
if (gene_mem (X, pow_ten, index_max, mem, result)) {
return reverse(init.s + result.s);
}
node init2 {0, ""};
nodes.push_back(init2);
while (1) {
for (node &x: nodes) {
x.s.push_back('0');
}
int n = nodes.size();
for (int i = 0; i < n; ++i) {
node y = nodes[i];
y.sum = (y.sum + pow_ten) % X;
if (used[y.sum]) continue;
used[y.sum] = true;
y.s.back() = '1';
if (y.sum != 0) {
int target = X - y.sum;
auto search = mem.find(target);
if (search != mem.end()) {
//std::cout << "mem size 2nd step = " << nodes.size() << "\n";
return reverse(init.s + search->second + y.s);
}
}
nodes.push_back(y);
}
pow_ten = (pow_ten * ten) % X;
}
}
int main() {
std::cout << "input number: ";
int n;
std::cin >> n;
std::string res;
auto t1 = std::chrono::high_resolution_clock::now();
res = multiple2(n),
std::cout << "Result = " << res << " ndigit = " << res.size() << std::endl;
auto t2 = std::chrono::high_resolution_clock::now();
auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();
std::cout << "time = " << duration2/1000 << " ms" << std::endl;
return 0;
}
For people more familiar with Python, here is a converted version of #Damien's code. Damien's important insight is to strongly reduce the search tree, taking advantage of the fact that each partial sum only needs to be investigated once, namely the first time it is encountered.
The problem is also described at Mathpuzzle, but there they mostly fix on the necessary existence of a solution. There's also code mentioned at the online encyclopedia of integer sequences. The sage version seems to be somewhat similar.
I made a few changes:
Starting with an empty list helps to correctly solve A=1 while simplifying the code. The multiplication by 10 is moved to the end of the loop. Doing the same for 0 seems to be hard, as log10(0) is minus infinity.
Instead of alternating between nodes[range] and nodes[new_range], two different lists are used.
As Python supports integers of arbitrary precision, the partial results could be stored as decimal or binary numbers instead of as strings. This is not yet done in the code below.
from collections import namedtuple
node = namedtuple('node', 'sum str')
def find_multiple_ones_zeros(A):
nodes = [node(0, "")]
used = set()
pow_ten = 1
while True:
new_nodes = []
for x in nodes:
y = node(x.sum, "0" + x.str)
new_nodes.append(y)
next_sum = (x.sum + pow_ten) % A
y = node((x.sum + pow_ten) % A, x.str)
if next_sum in used:
continue
used.add(next_sum)
y = node(next_sum, "1" + x.str)
if next_sum == 0:
return y.str
new_nodes.append(y)
pow_ten = (pow_ten * 10) % A
nodes = new_nodes

Correctness of algorithm for computing diameter of a graph

I am a TA for an introductory CS course, and one question given to students was how to use BFS to determine the diameter of a unweighted, undirected graph. The students were told they wouldn't be graded for efficiency, so the expected answer was a brute force algorithm where they ran BFS from every node to every other node and returned the maximum distance from these BFS runs. The students were provided with a BFS method they could reference in their pseudocode which took as an input a node and returned two mappings: one from each node in the graph to its distance from the start node (called distmap), and one from each node to its 'parent node' along the shortest path from the input node (called parentmap).
One student wrote the following algorithm:
Choose an arbitrary node from the graph and run BFS from it.
Create a set Temp of all the nodes that are not values in parentmap (i.e. the leaves of the BFS tree)
Initialize max_dist to 0
For each node n in Temp:
Run BFS from n
For each value d in distmap:
IF d > max_dist THEN set max_dist equal to d
RETURN max_dist
I believe this answer is correct, but I am unable to prove it. Can someone prove why it works or provide a counterexample?
Maybe a slightly simpler counter-example:
It is quite clear that the maximum distance in this graph is between the green nodes (4), but if you start your BFS from the red node, Temp will consist of the two blue nodes only, which gives an incorrect "diameter" of 3.
Assuming that not being in a parentmap means being a leaf in a BFS tree, the algorithm is wrong.
Let the graph have 10 vertices and the following undirected edges:
0 1
0 4
1 2
1 3
2 3
2 6
2 7
3 8
4 5
4 6
5 9
6 7
6 8
7 8
7 9
One of the valid BFS trees (with root 0) is:
0 1
1 2
1 3
2 7
3 8
0 4
4 6
4 5
5 9
The leaves are 6, 7, 8, 9, so this solution returns 3.
That's wrong. The answer is 4 (it's the distance between 3 and 5).
Taking all furthest nodes doesn't work either for this test.
Instead of asking someone to find a counterexample, you can do it by generating millions of small random test cases and checking if the solution produces a correct answer. Here's the code I used to generate this case (it doesn't look very good, but it does the job):
pair<vector<int>, set<int>> bfs(int st, const vector<vector<int>>& g) {
int n = g.size();
vector<int> d(n, n);
d[st] = 0;
queue<int> q;
q.push(st);
set<int> parents;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int to : g[v])
if (d[to] > d[v] + 1) {
d[to] = d[v] + 1;
q.push(to);
parents.insert(v);
}
}
return {d, parents};
}
int get_max_dist(const vector<vector<int>>& g) {
int res = 0;
for (int i = 0; i < (int)g.size(); i++) {
auto cur = bfs(i, g).first;
for (int x : cur)
cerr << x << " ";
cerr << endl;
res = max(res, *max_element(cur.begin(), cur.end()));
}
cerr << endl;
return res;
}
int get_max_dist_weird(const vector<vector<int>>& g) {
auto p = bfs(0, g);
vector<int> cand;
int n = g.size();
for (int i = 0; i < n; i++)
if (!p.second.count(i))
cand.push_back(i);
int res = 0;
for (int i : cand) {
auto cur = bfs(i, g).first;
res = max(res, *max_element(cur.begin(), cur.end()));
}
return res;
}
vector<vector<int>> rand_graph(int n) {
vector<vector<int>> g(n);
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (rand() & 1) {
g[i].push_back(j);
g[j].push_back(i);
}
return g;
}
int main() {
for (int i = 1;; i++) {
int n = 10;
auto g = rand_graph(n);
int correct = get_max_dist(g);
int got = get_max_dist_weird(g);
if (correct != got) {
cerr << correct << " " << got << endl;
for (int v = 0; v < n; v++)
for (int j : g[v])
if (v < j)
cerr << v << " " << j << endl;
}
assert (get_max_dist_weird(g) == get_max_dist(g));
if (i % 1000 == 0)
cerr << i << endl;
}
}
Sure, you can't prove that the algorithm is correct this way, but it's very likely to find a counterexample if it isn't.

Stuck with DFS/BFS task (USACO silver)

competitive programming noob here. I've been trying to solve this question:
http://www.usaco.org/index.php?page=viewproblem2&cpid=646
The code I wrote only works with the first test case, and gives a Memory Limit Exceed error -- or ('!') for the rest of the test cases.
This is my code (accidently mixed up M and N):
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
using std::vector;
vector<int> check;
vector< vector<int> > A;
void dfs(int node)
{
check[node] = 1;
int siz = A[node].size();
for (int i = 0; i < siz; i++)
{
int y = A[node][i];
if (check[y] == 0)
{
dfs(y);
}
}
}
bool connected(vector<int> C)
{
for (int i = 1; i <= C.size() - 1; i++)
{
if (C[i] == 0)
{
return false;
}
}
return true;
}
int main()
{
freopen("closing.in", "r", stdin);
freopen("closing.out", "w", stdout);
ios_base::sync_with_stdio(false);
int M, N;
cin >> M >> N;
check.resize(M + 1);
A.resize(M + 1);
for (int i = 0; i < N; i++)
{
int u, v;
cin >> u >> v;
A[u].push_back(v); A[v].push_back(u);
}
dfs(1);
if (!connected(check)) {
cout << "NO" << "\n";
}
else {
cout << "YES" << "\n";
}
fill(check.begin(), check.end(), 0);
for (int j = 1; j < M; j++)
{
int node;
bool con = true;
cin >> node;
check[node] = -1;
for (int x = 1; x <= N; x++)
{
if (check[x] == 0)
{
dfs(x);
break;
}
}
if (!connected(check)) {
cout << "NO" << "\n";
}
else {
cout << "YES" << "\n";
}
for (int g = 1; g <= M; g++)
{
if (check[g] == 1)
{
check[g] = 0;
}
}
}
return 0;
}
basically,
void dfs(int node) searches through the bidirectional graph starting from node until it reaches a dead end, and for each node that is visited, check[node] will become 1.
(if visited -> 1, not visited -> 0, turned off -> -1).
bool connected(vector C) will take the check vector and see if there are any nodes that weren't visited. if this function returns true, it means that the graph is connected, and false if otherwise.
In the main function,
1) I save the bidirectional graph given in the task as an Adjacency list.
2) dfs through it first to see if the graph is initially connected (then print "Yes" or "NO") then reset check
3) from 1 to M, I take the input value of which barn would be closed, check[the input value] = -1, and dfs through it. After that, I reset the check vector, but keeping the -1 values so that those barns would be unavailable for the next loops of dfs.
I guess my algorithm makes sense, but why would this give an MLE, and how could I improve my solution? I really can't figure out why my code is giving MLEs.
Thanks so much!
Your DFS is taking huge load of stacks and thus causing MLE
Try to implement it with BFS which uses queue. Try to keep the queue as global rather than local.
Your approach will give you Time Limit Exceeded verdict. Try to solve it more efficiently. Say O(n).

Counting tilings of a rectangle

I am trying to solve this problem but I can't find a solution:
A board consisting of squares arranged into N rows and M columns is given. A tiling of this board is a pattern of tiles that covers it. A tiling is interesting if:
only tiles of size 1x1 and/or 2x2 are used;
each tile of size 1x1 covers exactly one whole square;
each tile of size 2x2 covers exactly four whole squares;
each square of the board is covered by exactly one tile.
For example, the following images show a few interesting tilings of a board of size 4 rows and 3 columns:
http://dabi.altervista.org/images/task.img.4x3_tilings_example.gif
Two interesting tilings of a board are different if there exists at least one square on the board that is covered with a tile of size 1x1 in one tiling and with a tile of size 2x2 in the other. For example, all tilings shown in the images above are different.
Write a function
int count_tilings(int N, int M);
that, given two integers N and M, returns the remainder modulo 10,000,007 of the number of different interesting tilings of a board of size N rows and M columns.
Assume that:
N is an integer within the range [1..1,000,000];
M is an integer within the range [1..7].
For example, given N = 4 and M = 3, the function should return 11, because there are 11 different interesting tilings of a board of size 4 rows and 3 columns:
http://dabi.altervista.org/images/task.img.4x3_tilings_all.gif
for (4,3) the result is 11, for (6,5) the result is 1213.
I tried the following but it doesn't work:
static public int count_tilings ( int N,int M ) {
int result=1;
if ((N==1)||(M==1)) return 1;
result=result+(N-1)*(M-1);
int max_tiling= (int) ((int)(Math.ceil(N/2))*(Math.ceil(M/2)));
System.out.println(max_tiling);
for (int i=2; i<=(max_tiling);i++){
if (N>=2*i){
int n=i+(N-i);
int k=i;
//System.out.println("M-1->"+(M-1) +"i->"+i);
System.out.println("(M-1)^i)->"+(Math.pow((M-1),i)));
System.out.println( "n="+n+ " k="+k);
System.out.println(combinations(n, k));
if (N-i*2>0){
result+= Math.pow((M-1),i)*combinations(n, k);
}else{
result+= Math.pow((M-1),i);
}
}
if (M>=2*i){
int n=i+(M-i);
int k=i;
System.out.println("(N-1)^i)->"+(Math.pow((N-1),i)));
System.out.println( "n="+n+ " k="+k);
System.out.println(combinations(n, k));
if (M-i*2>0){
result+= Math.pow((N-1),i)*combinations(n, k);
}else{
result+= Math.pow((N-1),i);
}
}
}
return result;
}
static long combinations(int n, int k) {
/*binomial coefficient*/
long coeff = 1;
for (int i = n - k + 1; i <= n; i++) {
coeff *= i;
}
for (int i = 1; i <= k; i++) {
coeff /= i;
}
return coeff;
}
Since this is homework I won't give a full solution, but I'll give you some hints.
First here's a recursive solution:
class Program
{
// Important note:
// The value of masks given here is hard-coded for m == 5.
// In a complete solution, you need to calculate the masks for the
// actual value of m given. See explanation in answer for more details.
int[] masks = { 0, 3, 6, 12, 15, 24, 27, 30 };
int CountTilings(int n, int m, int s = 0)
{
if (n == 1) { return 1; }
int result = 0;
foreach (int mask in masks)
{
if ((mask & s) == 0)
{
result += CountTilings(n - 1, m, mask);
}
}
return result;
}
public static void Main()
{
Program p = new Program();
int result = p.CountTilings(6, 5);
Console.WriteLine(result);
}
}
See it working online: ideone
Note that I've added an extra parameter s. This stores the contents of the first column. If the first column is empty, s = 0. If the first column contains some filled squares the corresponding bits in s are set. Initially s = 0, but when a 2 x 2 tile is placed, this fills up some squares in the next column, and that will mean that s will be non-zero in the recursive call.
The masks variable is hard-coded but in a complete solution it needs to be calculated based on the actual value of m. The values stored in masks make more sense if you look at their binary representations:
00000
00011
00110
01100
01111
11000
11011
11110
In other words, it's all the ways of setting pairs of bits in a binary number with m bits. You can write some code to generate all these possiblities. Or since there are only 7 possible values of m, you could also just hard-code all seven possibilities for masks.
There are however two serious problems with the recursive solution.
It will overflow the stack for large values of N.
It requires exponential time to calculate. It is incredibly slow even for small values of N
Both these problems can be solved by rewriting the algorithm to be iterative. Keep m constant and initalize the result for n = 1 for all possible values of s to be 1. This is because if you only have one column you must use only 1x1 tiles, and there is only one way to do this.
Now you can calculate n = 2 for all possible values of s by using the results from n = 1. This can be repeated until you reach n = N. This algorithm completes in linear time with respect to N, and requires constant space.
Here is a recursive solution:
// time used : 27 min
#include <set>
#include <vector>
#include <iostream>
using namespace std;
void placement(int n, set< vector <int> > & p){
for (int i = 0; i < n -1 ; i ++){
for (set<vector<int> > :: iterator j = p.begin(); j != p.end(); j ++){
vector <int> temp = *j;
if (temp[i] == 1 || temp[i+1] == 1) continue;
temp[i] = 1; temp[i+1] = 1;
p.insert(temp);
}
}
}
vector<vector<int> > placement( int n){
if (n > 7) throw "error";
set <vector <int> > p;
vector <int> temp (n,0);
p.insert (temp);
for (int i = 0; i < 3; i ++) placement(n, p);
vector <vector <int> > s;
s.assign (p.begin(), p.end());
return s;
}
bool tryput(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if ((board[current][i] == 1 || board[current+1][i]) && comb[i] == 1) return false;
}
return true;
}
void put(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if (comb[i] == 1){
board[current][i] = 1;
board[current+1][i] = 1;
}
}
return;
}
void undo(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if (comb[i] == 1){
board[current][i] = 0;
board[current+1][i] = 0;
}
}
return;
}
int place (vector <vector <int> > &board, int current, vector < vector <int> > & all_comb){
int m = board.size();
if (current >= m) throw "error";
if (current == m - 1) return 1;
int count = 0;
for (int i = 0; i < all_comb.size(); i ++){
if (tryput(board, current, all_comb[i])){
put(board, current, all_comb[i]);
count += place(board, current+1, all_comb) % 10000007;
undo(board, current, all_comb[i]);
}
}
return count;
}
int place (int m, int n){
if (m == 0) return 0;
if (m == 1) return 1;
vector < vector <int> > all_comb = placement(n);
vector <vector <int> > board(m, vector<int>(n, 0));
return place (board, 0, all_comb);
}
int main(){
cout << place(3, 4) << endl;
return 0;
}
time complexity O(n^3 * exp(m))
to reduce the space usage try bit vector.
to reduce the time complexity to O(m*(n^3)), try dynamic programming.
to reduce the time complexity to O(log(m) * n^3) try divide and conquer + dynamic programming.
good luck

High score in grid walk

There is an interesting game named one person game. It is played on a m*n grid. There is an non-negative integer in each grid cell. You start with a score of 0. You cannot enter a cell with an integer 0 in it. You can start and end the game at any cell you want (of course the number in the cell cannot be 0). At each step you can go up, down, left and right to the adjacent grid cell. The score you can get at last is the sum of the numbers on your path. But you can enter each cell at most once.
The aim of the game is to get your score as high as possible.
Input:
The first line of input is an integer T the number of test cases. The first line of each test case is a single line containing 2 integers m and n which is the number of rows and columns in the grid. Each of next the m lines contains n space-separated integers D indicating the number in the corresponding cell
Output:
For each test case output an integer in a single line which is maximum score you can get at last.
Constraints:
T is less than 7.
D is less than 60001.
m and n are less than 8.
Sample Input:
4
1 1
5911
1 2
10832 0
1 1
0
4 1
0
8955
0
11493
Sample Output:
5911
10832
0
11493
I tried it but my approach is working very slow for a 7x7 grid.I am trying to access every possible path of the grid recursively and comparing the sum of every path.Below is my code
#include<iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
int max(int a,int b,int c, int d)
{
int max = a;
if(b>max)
max = b;
if(c>max)
max = c;
if(d>max)
max = d;
return max;
}
int Visit_Component( int (*A)[8], int Visit[8][8], int m,int n , int row, int col)
{
if ( ( row >= m ) || (col >= n ) || (col < 0) || (row < 0) || A[row][col] == 0 || Visit[row][col] == 1 )
{
return 0;
}
else
{
Visit[row][col] = 1;
int a= 0,b=0,c=0,d=0,result =0;
a = Visit_Component( A, Visit,m,n, row+1, col);
b = Visit_Component( A, Visit,m,n, row, col +1);
c = Visit_Component( A, Visit,m,n, row, col -1);
d = Visit_Component( A, Visit,m,n, row-1, col );
Visit[row][col] = 0;
result = A[row][col] + max(a,b,c,d);
return result;
}
}
int main(){
int T;
scanf("%d",&T);
for(int k =0; k<T;k++)
{
int N ;
int M;
int count = 0;
int maxcount = 0;
scanf("%d %d",&M,&N);
int C[8][8];
int visit[8][8];
for(int i = 0; i < M; i++)
for(int j = 0; j < N; j++)
{
scanf("%d",&C[i][j]);
visit[i][j] = 0;
}
for( int i= 0 ; i< M ; i++ )
{
for( int j =0; j< N ; j++ )
{
count = Visit_Component( C, visit,M,N, i, j);
if(count > maxcount)
{
maxcount = count;
}
}
}
printf("%d\n",maxcount);
}
return 0;
}
Please suggest me how to optimize this approach or a better algorithm.
As Wikipedia article on Travelling salesman problem suggests, there are exact algorithms, solving this task quickly. But it is hard to find any. And they are, most likely, complicated.
As for optimizing OP's approach, there are several possibilities.
It's easier to start with simple micro-optimization: condition Visit[row][col] == 1 is satisfied with highest probability, so it should come first.
Also it is reasonable to optimize branch-and-bound algorithm with dynamic programming to avoid some repeated calculations. Memorizing calculation results in simple hash table for the cases of up to 19 visited cells improves performance by more than 25% (and more may be expected for some improved hash table). Here is the modified code snippet:
#include<iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
int max(int a,int b,int c, int d)
{
int max = a;
if(b>max)
max = b;
if(c>max)
max = c;
if(d>max)
max = d;
return max;
}
typedef unsigned long long ull;
static const int HS = 10000019;
static const int HL = 20;
struct HT {
ull v;
int r;
int c;
};
HT ht[HS] = {0};
int Visit_Component(
int (*A)[8], ull& Visit, int m,int n , int row, int col, int x)
{
if ( (Visit & (1ull << (8*row+col))) || ( row >= m ) || (col >= n ) ||
(col < 0) || (row < 0) || A[row][col] == 0)
{
return 0;
}
else
{
if (x < HL)
{
HT& h = ht[(Visit+4*row+col)%HS];
if (h.v == Visit && h.r == row && h.c == col)
return 0;
}
Visit |= (1ull << (8*row+col));
int a= 0,b=0,c=0,d=0,result =0;
a = Visit_Component( A, Visit,m,n, row+1, col, x+1);
b = Visit_Component( A, Visit,m,n, row, col +1, x+1);
c = Visit_Component( A, Visit,m,n, row, col -1, x+1);
d = Visit_Component( A, Visit,m,n, row-1, col , x+1);
Visit &= ~(1ull << (8*row+col));
result = A[row][col] + max(a,b,c,d);
if (x < HL)
{
HT& h = ht[(Visit+4*row+col)%HS];
h.v = Visit;
h.r = row;
h.c = col;
}
return result;
}
}
int main(){
int T;
scanf("%d",&T);
for(int k =0; k<T;k++)
{
int N ;
int M;
int count = 0;
int maxcount = 0;
scanf("%d %d",&M,&N);
int C[8][8];
ull visit = 0;
for(int i = 0; i < M; i++)
for(int j = 0; j < N; j++)
{
scanf("%d",&C[i][j]);
}
for( int i= 0 ; i< M ; i++ )
{
for( int j =0; j< N ; j++ )
{
count = Visit_Component( C, visit,M,N, i, j, 0);
if(count > maxcount)
{
maxcount = count;
}
}
}
printf("%d\n",maxcount);
}
return 0;
}
And much more improvements may be done by pre-processing the input matrix. If there are no zeros in the matrix or if there is only one zero in the corner, you may just sum all the values.
If there is only one zero value (not in the corner), at most one non-zero value should be excluded from the sum. If you invent an algorithm, that determines the subset of cells, from which one of the cells must be removed, you can just select the smallest value from this subset.
If there are two or more zero values, use branch-and-bound algorithm: in this case it is about 20 times faster, because each zero value in input matrix means approximately fivefold speed increase.
One optimization that I can think of is to apply Dijkstra's algorithm. This algorithm will give you a minimum (in your case maximum) path for a particular source node to all destination nodes.
In this example, the first step would be to build a graph.
And because you don't know the source node to start at, you will have to apply Dijkstra's algorithm for each node in the grid. The time complexity will be better than your recursion method because for a particular source node, when finding a maximum path Dijkstra's algorithm does not go through all the possible paths.
#include<iostream>
#include<vector>
using namespace std;
vector<vector<int> >A;
vector<vector<bool> >test;
vector<vector<bool> >test1;
int sum_max=0;
int m,n;
vector<vector<bool> > stamp;
void color1(int i,int j,vector<vector<bool> >temp_vector,vector<vector<bool> > st,int summ){
temp_vector[i][j]=false;summ+=A[i][j];st[i][j]=true;
//1.1
if(i+1<m && temp_vector[i+1][j]){
if(test1[i+1][j]){
if(sum_max<(summ)){sum_max=summ;stamp=st;}
}
else{color1(i+1,j,temp_vector,st,summ);}
}
//1.2
if(i+1<m){if(!temp_vector[i+1][j]){ if(sum_max<(summ)){sum_max=summ;}}}
if(i+1>=m){if(sum_max<(summ)){sum_max=summ;}}
//2
if(i-1>=0 && temp_vector[i-1][j]){
if(test1[i-1][j]){
if(sum_max<(summ)){sum_max=summ;}
}
else{ color1(i-1,j,temp_vector,st,summ);}
}
//2.2
if(i-1>=0){if(!temp_vector[i-1][j]){ if(sum_max<(summ)){sum_max=summ;}}}
if(i-1<0){if(sum_max<(summ)){sum_max=summ;}}
//3
if(j+1<n && temp_vector[i][j+1]){
if(test1[i][j+1]){
if(sum_max<(summ)){sum_max=summ;}
}
else{ color1(i,j+1,temp_vector,st,summ);}}
//3.2
if(j+1<n){if(!temp_vector[i][j+1]){ if(sum_max<(summ)){sum_max=summ;}}}
if(j+1>=n){if(sum_max<(summ)){sum_max=summ;}}
//4
if(j-1>=0 && temp_vector[i][j-1]){
if(test1[i][j-1]){
if(sum_max<(summ)){sum_max=summ;}
}
else{ color1(i,j-1,temp_vector,st,summ);}}
//4.2
if(j-1>=0){if(!temp_vector[i][j-1]){ if(sum_max<(summ)){sum_max=summ;}}}
if(j+1<0){if(sum_max<(summ)){sum_max=summ;}}
}
void color(int i,int j){
test[i][j]=false;
if(i+1<m && test[i+1][j]){
color(i+1,j);}
if(i-1>=0 && test[i-1][j]){
color(i-1,j);
}
if(j+1<n && test[i][j+1]){
color(i,j+1);}
if(j-1>=0 && test[i][j-1]){color(i,j-1);}
}
int main(){
int tc;cin>>tc;
for(int i=0;i<tc;i++){
int mp,np;
cin>>mp;
cin>>np;m=mp;n=np;A.resize(m);test.resize(m);test1.resize(m);int sum=0;
vector<bool> ha1(m,1);
vector<bool> ha2(n,1);
for(int i=0;i<m;i++){A[i].resize(n);test[i].resize(n);test1[i].resize(n);
for(int j=0;j<n;j++){
cin>>A[i][j];sum+=A[i][j];
test[i][j]=true;test1[i][j]=false;
if(A[i][j]==0){test[i][j]=false;ha1[i]=false;ha2[j]=false;}
}
}cout<<endl;
for(int i=0;i<m;i++){cout<<" "<<ha1[i];} cout<<endl;
for(int i=0;i<n;i++){cout<<" "<<ha2[i];} cout<<endl;
cout<<"sum "<<sum<<"\n";
int temp_sum=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){//if(A[i][j]<=8845){cout<<"\nk "<<A[i][j]<<" "<<(8845-A[i][j]);}
if(test[i][j]){
if((i-1)>=0 && test[i-1][j] && (i+1)<m && test[i+1][j] && (j-1)>=0 && test[i][j-1] && (j+1)<n && test[i][j+1] && test[i-1][j-1] && test[i-1][j+1]&& test[i+1][j-1] && test[i+1][j+1]){
temp_sum+=A[i][j];test1[i][j]=true;}
}
// cout<<test1[i][j]<<" ";
}//cout<<"\n";
}
// /*
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(test1[i][j]){if(!((test1[i-1][j]||test1[i+1][j]) && (test1[i][j-1]||test1[i][j+1]))){
temp_sum-=A[i][j]; test1[i][j]=false;}
}
//
// cout<<test1[i][j]<<" ";
}//
// cout<<"\n";
}
// */
//cout<<"\n temp_sum is "<<temp_sum<<endl;
vector<vector<bool> > st(m,vector<bool>(n,0));st=test1;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(test[i][j] && (!test1[i][j])){
color1(i,j,test,st,0);
}}}
// cout<<"\nsum is "<<(sum_max+temp_sum)<<endl<<endl;
cout<<(sum_max+temp_sum)<<endl;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){cout<<stamp[i][j]<<" ";} cout<<endl;}
// cout<<max<<endl;
A.clear();
test.clear();
test1.clear();
sum_max=0;
}
cout<<endl;system("pause");
return 0;
}

Resources