shortest path going through m edges - algorithm

Hello there smart people.
I have a following graph problem.
Given a complete, directed, wieghted graph with n vertices, find the length of the shortest path (starting at any vertex) going through m - 1 edges (edges in the path might repeat).
As for the limits n <= 200, m <= 1e9.
Looking at the limits I can say there must be some clever way without some sort of dp and graph traversing but I just cant think of anything like that.
Thanks in advance.
Example:
n = 3, m = 5
edges:
1 -> 2 weight = 10,
1 -> 3 weight = 100,
2 -> 1 weight = 10,
2 -> 3 weight = 50,
3 -> 1 weight = 30,
3 -> 2 weight = 70,
answer would be 40 (1 -> 2 -> 1 -> 2 -> 1)

A naive solution would be to run BFS (breadth-first search) until the mth level and maintain the minimum sum of weights and return it.
But in the question it says we can include the vertex as many times till there is a path between them, so now we can do the below steps:
Calculate all the cycles present in the graph, we can reuse these cycles in calculating the minimum weight possible.
for example:
in the question, there is a cycle present 1-->2-->1 of length=3 weight=20, the value of m = 5, now we can use this path twice, but if m was 6 then we are left with 1 more node to include.
Now we can calculate the min path (to include leftover nodes) of length l (which is 1 if m=6) from 1 and add it to the above weight. (which will we 1-->2 =10)
Repeat the steps 1 and 2 for every cycle present in the graph and maintain the minimum sum.
Below is the c++ code describing the above solution (it may not be 100% correct, but you will get the basic idea)
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
struct Edge{
int src, dest, weight;
};
struct Node{
int start_vertex, end_vertex, weight, edge_count=0;
};
class Graph{
public:
vector<vector<pair<int, int>>> adjList;
int V;
Graph(vector<Edge> edges, int V){
adjList.resize(V+1);
this->V = V;
for(Edge edge:edges){
adjList[edge.src].push_back({edge.dest, edge.weight});
}
}
};
int BFS(Graph &g, int m){
queue<Node> Q;
vector<Node> cycles;
// to store min path from vertex i of length j
vector<vector<int>> dp(g.V+1, vector<int>(g.V+1, INT_MAX));
for(int i=0; i<=g.V; i++)
dp[i][0] = 0;
for(int i=1; i<=g.V; i++){
Q.push({i, i, 0, 1});
}
while(!Q.empty()){
Node top = Q.front();
Q.pop();
if(top.edge_count >= g.V) break;
int v = top.end_vertex;
int start_vertex = top.start_vertex;
int weight = top.weight;
int edge_count = top.edge_count;
for(auto x:g.adjList[v]){
// finding all the cycles
if(x.first == start_vertex){
Node n = {start_vertex, v, weight+x.second, edge_count+1};
cycles.push_back(n);
}else{
Q.push({start_vertex, x.first, weight+x.second, edge_count+1});
}
if(dp[start_vertex][edge_count] > weight+x.second){
dp[start_vertex][edge_count] = weight+x.second;
}
}
}
// finding minimum:
int min_weight = INT_MAX;
if(m<=g.V){
for(int i=1; i<=g.V; i++){
min_weight = min(min_weight, dp[i][m]);
}
}
// checking all the cycles for reusability and maintaining min sum
for(int i=0; i<cycles.size(); i++){
int sum = cycles[i].weight;
int length_left_to_cover = m-cycles[i].edge_count;
sum += length_left_to_cover/(cycles[i].edge_count-1) * cycles[i].weight;
int vertices_left_to_include = 0;
if(m-cycles[i].edge_count>0){
vertices_left_to_include = (m-cycles[i].edge_count)%(cycles[i].edge_count-1);
}
min_weight = min(min_weight, sum+dp[cycles[i].start_vertex][vertices_left_to_include]);
}
return min_weight;
}
// 1 -> 2 weight = 10,
// 1 -> 3 weight = 100,
// 2 -> 1 weight = 10,
// 2 -> 3 weight = 50,
// 3 -> 1 weight = 30,
// 3 -> 2 weight = 70,
int main(){
vector<Edge> edges = {
{1, 2, 10},
{1, 3, 100},
{2, 1, 10},
{2, 3, 50},
{3, 1, 30},
{3, 2, 70}
};
int V = 3;
int m = 5;
Graph g(edges, V);
cout<<"Min weight: "<<BFS(g, m);
}
Output:
Min weight: 40

Related

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))

2D Peak Algorithm fails to find the peak

I just started an MIT course on algorithm, and we were taught the 2D Peak Finding algo. I tried dry running and implementing it yet the algo seems to be failing for this input.
{5, 0, 3, 2}
{1, 1, 2, 4}
{1, 2, 4, 4}
This is the Algorithm:
• Pick middle column j = m/2
• Find global maximum on column j at (i,j)
• Compare(i,j−1),(i,j),(i,j+1)
• Pick left columns of(i,j−1)>(i,j)
• Similarly for right
• (i,j) is a 2D-peak if neither condition holds ← WHY?
• Solve the new problem with half the number of columns.
• When you have a single column, find global maximum and you‘re done.
Update, Here is the code which I tried and doesn't seem to be working:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 100;
int findMax(int arr[][MAX], int rows, int mid, int& max)
{
int max_index = 0;
for (int i = 0; i < rows; i++) {
if (max < arr[i][mid]) {
max = arr[i][mid];
max_index = i;
}
}
return max_index;
}
int findPeakRec(int arr[][MAX], int rows, int columns, int mid)
{
int max = 0;
int max_index = findMax(arr, rows, mid, max);
if (mid == 0 || mid == columns - 1)
return max;
if (max >= arr[max_index][mid - 1] && max >= arr[max_index][mid + 1])
return max;
if (max < arr[max_index][mid - 1])
return findPeakRec(arr, rows, columns, mid - ceil((double)mid / 2));
return findPeakRec(arr, rows, columns, mid + ceil((double)mid / 2));
}
int findPeak(int arr[][MAX], int rows, int columns)
{
return findPeakRec(arr, rows, columns, columns / 2);
}
int main()
{
int arr[][MAX] = { { 5, 0, 3, 2 },
{ 1, 1, 2, 4 },
{ 1, 2, 4, 4 },
{ 3, 2, 0, 1 } };
int rows = 4, columns = 4;
cout << findPeak(arr, rows, columns);
return 0;
}
this is how I implemented the algorithm.
The algorithm is correct (Just a spelling mistake in the fourth bullet point: "of" should read "if").
You missed a correct definition of "peak". The algorithm for peak finding intends to find a local maximum, not necessarily the global maximum. For a global maximum the algorithm is trivial, you just look for the maximum value with a row by row scan.
But peak finding can be more efficient as not all values need to be inspected.

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;
}

Bridge in a graph that makes the graph disconnected

I am working on programming where I need to find the articulation points of a graph (nodes such that removing any of them makes the graph disconnected)
For example, I have these links:
Example 1
[[0,1], [0,2], [1,3], [2,3], [5,6], [3,4]]
The answer should be [2,3,5], because removing these nodes makes the graph disconnected.
Explanation:
If I remove node 2 here, the graph becomes 2 parts 0,1,3,4 and 5,6
If I remove node 3 here, the graph becomes 2 parts 0,1,2,5,6 and 4
If I remove node 5 here, the graph becomes 2 parts 0,1,2,3,4 and 6
Example 2:
[[1,2], [2,3], [3,4], [4,5], [6,3]]
The output should be: [2, 3, 4]
Explanation:
If I remove node 2 here, the graph becomes 2 parts 1, and 3,4,5,6
If I remove node 3 here, the graph becomes 3 parts 1,2 and 6 and 4,5
If I remove node 4 here, the graph becomes 2 parts 1,2,3,6 and 5
How to achieve this in a Java program?
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.List;
public class ArticulationPointsAdjacencyList {
private int n, id, rootNodeOutcomingEdgeCount;
private boolean solved;
private int[] low, ids;
private boolean[] visited, isArticulationPoint;
private List<List<Integer>> graph;
public ArticulationPointsAdjacencyList(List<List<Integer>> graph, int n) {
if (graph == null || n <= 0 || graph.size() != n) throw new IllegalArgumentException();
this.graph = graph;
this.n = n;
}
// Returns the indexes for all articulation points in the graph even if the
// graph is not fully connected.
public boolean[] findArticulationPoints() {
if (solved) return isArticulationPoint;
id = 0;
low = new int[n]; // Low link values
ids = new int[n]; // Nodes ids
visited = new boolean[n];
isArticulationPoint = new boolean[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
rootNodeOutcomingEdgeCount = 0;
dfs(i, i, -1);
isArticulationPoint[i] = (rootNodeOutcomingEdgeCount > 1);
}
}
solved = true;
return isArticulationPoint;
}
private void dfs(int root, int at, int parent) {
if (parent == root) rootNodeOutcomingEdgeCount++;
visited[at] = true;
low[at] = ids[at] = id++;
List<Integer> edges = graph.get(at);
for (Integer to : edges) {
if (to == parent) continue;
if (!visited[to]) {
dfs(root, to, at);
low[at] = min(low[at], low[to]);
if (ids[at] <= low[to]) {
isArticulationPoint[at] = true;
}
} else {
low[at] = min(low[at], ids[to]);
}
}
}
/* Graph helpers */
// Initialize a graph with 'n' nodes.
public static List<List<Integer>> createGraph(int n) {
List<List<Integer>> graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
return graph;
}
// Add an undirected edge to a graph.
public static void addEdge(List<List<Integer>> graph, int from, int to) {
graph.get(from).add(to);
graph.get(to).add(from);
}
/* Example usage: */
public static void main(String[] args) {
testExample2();
}
private static void testExample1() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 0, 1);
addEdge (graph, 0, 2);
addEdge (graph, 1, 3);
addEdge (graph, 2, 3);
addEdge (graph, 2, 5);
addEdge (graph, 5, 6);
addEdge (graph, 3, 4);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 5 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
private static void testExample2() {
int n = 7;
List < List < Integer >> graph = createGraph (n);
addEdge (graph, 1, 2);
addEdge (graph, 2, 3);
addEdge (graph, 3, 4);
addEdge (graph, 3, 6);
addEdge (graph, 4, 5);
ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
boolean[] isArticulationPoint = solver.findArticulationPoints();
// Prints:
// Node 2 is an articulation
// Node 3 is an articulation
// Node 4 is an articulation
for (int i = 0; i < n; i++)
if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
}
}
Reference: https://github.com/williamfiset/Algorithms/blob/master/com/williamfiset/algorithms/graphtheory/ArticulationPointsAdjacencyList.java
There are different algorithms used to find the nodes such that if removed they make the graph disconnected (called articulation points).
Here I explain one of them and I provide some code that implements it:
Tarjan Algorithm
Given a graph we want to find all the such that if is removed from the graph become disconnected
The first observation is that the a (weak) connected component in a directed graph is equal to a connected component in the same graph, but where the edges are undirected. So for simplicity we consider as an undirected graph.
Algorithm description
On the graph we run a pre-order Depth First Search (DFS) visit where for any node we assign 2 values, let's call it pre and low. pre represent the instant when the node is visited and low the instant of the lowest reachable node from .
The visit works in this way:
At every step of the visit both pre and low of are set to the next value of pre. Then if we find that a cycle is being closed we set low to pre of the start cycle node. low value is transmitted to parent through DFS backtracking.
When the DFS finish for every couple of nodes such that and are neighbor and low value of is greater or equal to the pre value of then is an articulation point.
For this there is an exception: the root of the DFS spanning tree is an articulation point only if it has more than 1 children
Example
(In the graph P obviously means pre and L means low)
At first pre and low of every vertex are set to a default value (let's say -1)
We start from node 0 and set his pre and low
We go to node 1 and set his pre and low
We can go to 2 or 3, we decide to go to 2 and set his pre and low
We can go to 4 or 5, we decide to go to 4 and set his pre and low
We go to 3 and set his pre and low
We see that 1 is alredy visited; that means it is a cycle, so we update low of 3 to pre of 1
Through backtrack we return to 4 and update his low value
Through backtrack we return to 2 and update his low value
Now we go to 5 and set his pre and low
Through backtrack we return to 2, but there's nothing to do.
We returned from 5 so his low value is fixed and is greater than pre value of 2; so 2 is an articulation point
Through backtrack we return to 1, and there's nothing to do.
We returned from 2 so his low value is fixed and is equal to the pre value of 1; so 1 is an articulation point
Through backtrack we return to 0, but there's nothing to do.
We returned from 1 so his low value is fixed and is greater than pre value of 0; but 0 is the root and has only one child; so it isn't an articulation point
So we have found the answer: [1, 2]
Code
Here is a simple really easy to understand snippet of code (C++) extracted from Competitive Programming Handbook by S. Halim and F. Halim and modified by me.
It is not very adapt to "real word application" (for example because it uses global variables) but it is ok for competitive programming and explaining due to his brevity and clearness.
const int UNVISITED = -1;
vector<int> dfs_low;
vector<int> dfs_pre;
int dfsNumberCounter;
int rootChildren;
vector<vector<int>> AdjList;
vector<int> articulation_vertex;
// This function is the DFS that implement Tarjan algoritm
void articulationPoint(int u) {
dfs_low[u] = dfs_pre[u] = dfsNumberCounter++; // dfs_low[u] <= dfs_pre[u]
for (int j = 0; j < (int)AdjList[u].size(); j++) {
int v = AdjList[u][j];
if (dfs_pre[v] == UNVISITED) { // a tree edge
dfs_parent[v] = u;
if (u == dfsRoot) rootChildren++; // special case if u is a root
articulationPoint(v);
if (dfs_low[v] >= dfs_pre[u]) // for articulation point
articulation_vertex[u] = true; // store this information first
dfs_low[u] = min(dfs_low[u], dfs_low[v]); // update dfs_low[u]
}
else if (v != dfs_parent[u]) // a back edge and not direct cycle
dfs_low[u] = min(dfs_low[u], dfs_pre[v]); // update dfs_low[u]
} }
// Some driver code
int main() {
... //Init of variables and store of the graph inside AdjList is omitted
... // V is the number of nodes
dfsNumberCounter = 0;
dfs_pre.assign(V, UNVISITED);
dfs_low.assign(V, 0);
dfs_parent.assign(V, 0);
articulation_vertex.assign(V, 0);
rootChildren = 0;
articulationPoint(0);
if (root_children > 1) {
articulation_vertex[0] = false;
}
printf("Articulation Points:\n");
for (int i = 0; i < V; i++)
if (articulation_vertex[i])
printf(" Vertex %d\n", i);
}

The minimum number of coins the sum of which is S

Given a list of N coins, their values (V1, V2, ... , VN), and the total sum S. Find the minimum number of coins the sum of which is S (we can use as many coins of one type as we want), or report that it's not possible to select coins in such a way that they sum up to S.
I try to understand dynamic programming, haven't figured it out. I don't understand the given explanation, so maybe you can throw me a few hints how to program this task? No code, just ideas where I should start.
Thanks.
The precise answer to this problem is well explained here.
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg
This is a classic Knapsack problem, take a look here for some more information: Wikipedia Knapsack Problem
You should also look at some sorting, specifically sorting from Largest to Smallest values.
As already pointed out, Dynamic Programming suits best for this problem. I have written a Python program for this:-
def sumtototal(total, coins_list):
s = [0]
for i in range(1, total+1):
s.append(-1)
for coin_val in coins_list:
if i-coin_val >=0 and s[i-coin_val] != -1 and (s[i] > s[i-coin_val] or s[i] == -1):
s[i] = 1 + s[i-coin_val]
print s
return s[total]
total = input()
coins_list = map(int, raw_input().split(' '))
print sumtototal(total, coins_list)
For input:
12
2 3 5
The output would be:
[0, -1, 1, 1, 2, 1, 2, 2, 2, 3, 2, 3, 3]
3
The list_index is the total needed and the value at list_index is the no. of coins needed to get that total. The answer for above input(getting a value 12) is 3 ( coins of values 5, 5, 2).
I think the approach you want is like this:
You know that you want to produce a sum S. The only ways to produce S are to first produce S-V1, and then add a coin of value V1; or to produce S-V2 and then add a coin of value V2; or...
In turn, T=S-V1 is producible from T-V1, or T-V2, or...
By stepping back in this way, you can determine the best way, if any, to produce S from your Vs.
Question is already answered but I wanted to provide working C code that I wrote, if it helps anyone. enter code here
Code has hard coded input, but it is just to keep it simple. Final solution is the array min containing the number of coins needed for each sum.
#include"stdio.h"
#include<string.h>
int min[12] = {100};
int coin[3] = {1, 3, 5};
void
findMin (int sum)
{
int i = 0; int j=0;
min [0] = 0;
for (i = 1; i <= sum; i++) {
/* Find solution for Sum = 0..Sum = Sum -1, Sum, i represents sum
* at each stage */
for (j=0; j<= 2; j++) {
/* Go over each coin that is lesser than the sum at this stage
* i.e. sum = i */
if (coin[j] <= i) {
if ((1 + min[(i - coin[j])]) <= min[i]) {
/* E.g. if coin has value 2, then for sum i = 5, we are
* looking at min[3] */
min[i] = 1 + min[(i-coin[j])];
printf("\nsetting min[%d] %d",i, min[i]);
}
}
}
}
}
void
initializeMin()
{
int i =0;
for (i=0; i< 12; i++) {
min[i] = 100;
}
}
void
dumpMin()
{
int i =0;
for (i=0; i< 12; i++) {
printf("\n Min[%d]: %d", i, min[i]);
}
}
int main()
{
initializeMin();
findMin(11);
dumpMin();
}
I don't know about dynamic programming but this is how I would do it. Start from zero and work your way to S. Produce a set with one coin, then with that set produce a two-coin set, and so on... Search for S, and ignore all values greater than S. For each value remember the number of coins used.
Lots of people already answered the question. Here is a code that uses DP
public static List<Integer> getCoinSet(int S, int[] coins) {
List<Integer> coinsSet = new LinkedList<Integer>();
if (S <= 0) return coinsSet;
int[] coinSumArr = buildCoinstArr(S, coins);
if (coinSumArr[S] < 0) throw new RuntimeException("Not possible to get given sum: " + S);
int i = S;
while (i > 0) {
int coin = coins[coinSumArr[i]];
coinsSet.add(coin);
i -= coin;
}
return coinsSet;
}
public static int[] buildCoinstArr(int S, int[] coins) {
Arrays.sort(coins);
int[] result = new int[S + 1];
for (int s = 1; s <= S; s++) {
result[s] = -1;
for (int i = coins.length - 1; i >= 0; i--) {
int coin = coins[i];
if (coin <= s && result[s - coin] >= 0) {
result[s] = i;
break;
}
}
}
return result;
}
The main idea is - for each coin j, value[j] <= i (i.e sum) we look at the minimum number of coins found for i-value[j] (let say m) sum (previously found). If m+1 is less than the minimum number of coins already found for current sum i then we update the number of coins in the array.
For ex - sum = 11 n=3 and value[] = {1,3,5}
Following is the output we get
i- 1 mins[i] - 1
i- 2 mins[i] - 2
i- 3 mins[i] - 3
i- 3 mins[i] - 1
i- 4 mins[i] - 2
i- 5 mins[i] - 3
i- 5 mins[i] - 1
i- 6 mins[i] - 2
i- 7 mins[i] - 3
i- 8 mins[i] - 4
i- 8 mins[i] - 2
i- 9 mins[i] - 3
i- 10 mins[i] - 4
i- 10 mins[i] - 2
i- 11 mins[i] - 3
As we can observe for sum i = 3, 5, 8 and 10 we improve upon from our previous minimum in following ways -
sum = 3, 3 (1+1+1) coins of 1 to one 3 value coin
sum = 5, 3 (3+1+1) coins to one 5 value coin
sum = 8, 4 (5+1+1+1) coins to 2 (5+3) coins
sum = 10, 4 (5+3+1+1) coins to 2 (5+5) coins.
So for sum=11 we will get answer as 3(5+5+1).
Here is the code in C. Its similar to pseudocode given in topcoder page whose reference is provided in one of the answers above.
int findDPMinCoins(int value[], int num, int sum)
{
int mins[sum+1];
int i,j;
for(i=1;i<=sum;i++)
mins[i] = INT_MAX;
mins[0] = 0;
for(i=1;i<=sum;i++)
{
for(j=0;j<num;j++)
{
if(value[j]<=i && ((mins[i-value[j]]+1) < mins[i]))
{
mins[i] = mins[i-value[j]] + 1;
printf("i- %d mins[i] - %d\n",i,mins[i]);
}
}
}
return mins[sum];
}
int getMinCoins(int arr[],int sum,int index){
int INFINITY=1000000;
if(sum==0) return 0;
else if(sum!=0 && index<0) return INFINITY;
if(arr[index]>sum) return getMinCoins(arr, sum, index-1);
return Math.min(getMinCoins(arr, sum, index-1), getMinCoins(arr, sum-arr[index], index-1)+1);
}
Consider i-th coin. Either it will be included or not. If it is included, then the value sum is decreased by the coin value and the number of used coins increases by 1. If it is not included, then we need to explore the remaining coins similarly. We take the minimum of two cases.
I knew this is a old question, but this is a solution in Java.
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class MinCoinChange {
public static void min(int[] coins, int money) {
int[] dp = new int[money + 1];
int[] parents = new int[money + 1];
int[] usedCoin = new int[money + 1];
Arrays.sort(coins);
Arrays.fill(dp, Integer.MAX_VALUE);
Arrays.fill(parents, -1);
dp[0] = 0;
for (int i = 1; i <= money; ++i) {
for (int j = 0; j < coins.length && i >= coins[j]; ++j) {
if (dp[i - coins[j]] + 1 < dp[i]) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
parents[i] = i - coins[j];
usedCoin[i] = coins[j];
}
}
}
int parent = money;
Map<Integer, Integer> result = new HashMap<>();
while (parent != 0) {
result.put(usedCoin[parent], result.getOrDefault(usedCoin[parent], 0) + 1);
parent = parents[parent];
}
System.out.println(result);
}
public static void main(String[] args) {
int[] coins = { 1, 5, 10, 25 };
min(coins, 30);
}
}
For a fast recursive solution, you can check this link: java solution
I am going through the minimum steps required to find the perfect coin combination.
Say we have coins = [20, 15, 7] and monetaryValue = 37. My solution will work as follow:
[20] -> sum of array bigger than 37? NO -> add it to itself
[20, 20] greater than 37? YES (20 + 20) -> remove last and jump to smaller coin
[20, 15] 35 OK
[20, 15, 15] 50 NO
[20, 15, 7] 42 NO
// Replace biggest number and repeat
[15] 15 OK
[15, 15] 30 OK
[15, 15, 15] 45 NO
[15, 15, 7] 37! RETURN NUMBER!
def leastCoins(lst, x):
temp = []
if x == 0:
return 0
else:
while x != 0:
if len(lst) == 0:
return "Not Possible"
if x % max(lst) == 0:
temp.append((max(lst), x//max(lst)))
x = 0
elif max(lst) < x:
temp.append((max(lst), x//max(lst)))
x = x % max(lst)
lst.remove(max(lst))
else:
lst.remove(max(lst))
return dict(temp)
leastCoins([17,18,2], 100652895656565)

Resources