Isolating some vertices in a weighted tree with minimum cost - c++

Suppose we are given a weighted tree and some set of vertices of that tree. The problem is to isolate those vertices(e.g. there should not be paths between them) by removing edges from the tree, such that the sum of the weights of removed edges is minimal.
I have been trying to solve this problem for about two hours, then I found solution in C++, but there was no explanation. As I understood it uses Dynamic Programming technique.
My question is what is the basic idea of solving this problem?
Thanks!
Here is the solution.
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define PB push_back
#define MP make_pair
typedef long long LL;
const int MAXN = 100005;
const LL inf = 1LL << 55;
int N, K;
bool bad[MAXN];
LL dp[MAXN][2];
vector<pair<int, int> > adj[MAXN];
void dfs(int u, int fa) {
dp[u][1] = 0;
dp[u][0] = bad[u] ? inf : 0;
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i].first, w = adj[u][i].second;
if (v != fa) {
dfs(v, u);
dp[u][1] += min(dp[v][0], dp[v][1] + w);
dp[u][1] = min(dp[u][1], dp[u][0] + dp[v][1]);
if (!bad[u])
dp[u][0] += min(dp[v][0], dp[v][1] + w);
}
}
}
int main() {
cin >> N >> K;
for (int i = 1; i < N; i++) {
int a, b, c;
cin >> a >> b >> c;
adj[a].PB(MP(b, c)); adj[b].PB(MP(a, c));
}
memset(bad, false, sizeof(bad));
for (int i = 0; i < K; i++) {
int x;
cin >> x;
bad[x] = true;
}
dfs(0, -1);
LL ret = min(dp[0][0], dp[0][1]);
cout << ret << endl;
return 0;
}

Formally, the problem is, given an acyclic undirected graph with weighted edges, together with a set of terminal vertices, find the minimum set of edges whose removal means that, for all pairs of distinct terminals, those terminals are no longer connected.
Root the graph arbitrarily and treat it as a general tree. We run a dynamic program that operates bottom-up on the vertices. Each vertex u has two labels: dp[u][0] is the minimum weight of edges to be removed in the subtree rooted at u so that no terminal in the subtree is connected to another terminal or to u, and dp[u][1] is the minimum removal weight that leaves exactly one subtree terminal connected to u. We have a recurrence
{ infinity if v is a terminal
dp[u][0] = { sum dp[v][0] otherwise
{ children v of u
dp[u][1] = min dp[v][1] + sum min(dp[w][0], dp[w][1] + weight(uw)),
children v of u other children w of v
taking an empty min to be infinity.

Related

Check if the system of equations have a solution

Question:
There are N variables x1, x2, x3, ...... xN.
There are M triplets such that triple {i,j,c} indicate that xi = xj + c and so each triplet denote an equation. Check if the system of equations have atleast one solution.
My idea:
I considered the system of equations as a graph of N nodes labelled from 1 to N. If {i,j,c} is a triplet then, i->j is an edge with weight c and j->i is an edge with weight -c. With 0 as the source, I applied Bellman Ford algorithm to detect any negative cycles. If there is any positive sum cycle, then there must also be a negative sum cycle, which can be traced with Bellman Ford.
Following is the code
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF (1<<30)
int main(){
int T;
cin>>T;
while(T--){
int N;int M;
cin>>N;cin>>M;
vector<vector<int>> edges;
for (int i=0;i<M;++i){
int first;int second;int third;
cin>>first;cin>>second;cin>>third;
first--;second--;
edges.push_back({first,second,third});
edges.push_back({second,first,-third});
}
vector<int> distances(N,INF);
distances[0] = 0;
for (int i=1;i<=N;++i){
for (int j=0;j<edges.size();++j){
int from = edges[j][0];
int to = edges[j][1];
int weight = edges[j][2];
if (distances[from]+weight<distances[to]){
distances[to] = distances[from] + weight;
}
}
}
int flag = 0;
for (int j=0;j<edges.size();++j){
int from = edges[j][0];
int to = edges[j][1];
int weight = edges[j][2];
if (distances[from]+weight<distances[to]){
flag = 1;
break;
}
}
if (flag==1){
cout << "NO" << endl;
}
else{
cout << "YES" << endl;
}
}
}
Is the idea correct? I am unable to generate test cases against the above logic. But, the code failed to give correct output for many test cases.
Can somebody point out the mistake.
Thank you

hostel visit question (priority queue application )

question: Dean of MAIT is going to visit Hostels of MAIT. As you know that he is a very busy person so he decided to visit only the first "K" nearest Hostels. Hostels are situated on a 2D plane. You are given the coordinates of hostels and you have to answer the Rocket distance of Kth nearest hostel from the origin ( Dean's place )
Input Format
The first line of input contains Q Total no. of queries and K There are two types of queries:
first type: 1 x y For query of 1st type, you came to know about the coordinates ( x, y ) of the newly constructed hostel. second type: 2 For query of 2nd type, you have to output the Rocket distance of Kth nearest hostel till now.
//The Dean will always stay at his place ( origin ). It is guaranteed that there will be at least k queries of type 1 before the first query of type 2.
Rocket distance between two points ( x2 , y2 ) and ( x1 , y1 ) is defined as (x2 - x1)2 + (y2 - y1)2
Constraints
1 < = k < = Q < = 10^5 -10^6 < = x , y < = 10^6
Output Format
For each query of type 2 output the Rocket distance of Kth nearest hostel from Origin.//
This is my code:
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
class roomno
{
public:
int x;
int y;
roomno(int x,int y)
{
this->x=x;
this->y=y;
}
void print()
{
cout<<"location"<<"("<<x<<","<<y<<")"<<endl;
}
int distance ()
{
return (x*x+y*y);
}
};
class roomcompare
{
public:
bool operator() (roomno r1,roomno r2)
{
return r1.distance()>r2.distance();
}
};
int main()
{
int x[1000]},y[1000];
int l,k=0;
priority_queue<roomno,vector<roomno>,roomcompare> pq;
int n,i,j;
cin>>n>>l;
//cin>>n;
cin.ignore();
for( i=0;i<n;i++)
{
cin>>x[i];
}
cin.ignore();
for( j=0;j<n;j++)
{
cin>>y[j];
}
cin.ignore();
for(i=0;i<n;i++ )
{
roomno r1(x[i],y[i]);
pq.push(r1);
}
while(!pq.empty()&&k!=l)
{ k++;
roomno r2=pq.top();
r2.print();
pq.pop();
}
return 0;
}
Original link to code: https://codeshare.io/2j1bkA
What's wrong with my code?
Please consider adding what problem you are facing in your post. I've seen your code but I couldn't help with your code as I even saw a syntax error in your code and didn't know if it was the problem.
If I didn't misunderstand your question, std::set will fit better than priority queue as it is not possible to remove the largest item in a priority queue(min heap). The following code should work:
#include <iostream>
#include <set>
using namespace std;
using ll = long long;
multiset<ll> distances;
int main() {
ll n, k;
cin >> n >> k;
for(ll i = 0; i < n; ++i) {
ll query;
cin >> query;
if (query == 1) {
ll x, y;
cin >> x >> y;
distances.insert(x * x + y * y);
if (distances.size() > k) {
distances.erase(--distances.end());
}
} else {
cout << *distances.rbegin() << '\n';
}
}
return 0;
}

Solving Wormholes (ZCO 2012)

Description:
The year is 2102 and today is the day of ZCO. This year there are N contests and the starting and ending times of each contest is known to you. You have to participate in exactly one of these contests. Different contests may overlap. The duration of different contests might be different.
There is only one examination centre. There is a wormhole V that transports you from your house to the examination centre and another wormhole W that transports you from the examination centre back to your house. Obviously, transportation through a wormhole does not take any time; it is instantaneous. But the wormholes can be used at only certain fixed times, and these are known to you.
So, you use a V wormhole to reach the exam centre, possibly wait for some time before the next contest begins, take part in the contest, possibly wait for some more time and then use a W wormhole to return back home. If you leave through a V wormhole at time t1 and come back through a W wormhole at time t2, then the total time you have spent is (t2 - t1 + 1). Your aim is to spend as little time as possible overall while ensuring that you take part in one of the contests.
You can reach the centre exactly at the starting time of the contest, if possible. And you can leave the examination centre the very second the contest ends, if possible. You can assume that you will always be able to attend at least one contest–that is, there will always be a contest such that there is a V wormhole before it and a W wormhole after it.
For instance, suppose there are 3 contests with (start,end) times (15,21), (5,10), and (7,25), respectively. Suppose the V wormhole is available at times 4, 14, 25, 2 and the W wormhole is available at times 13 and 21. In this case, you can leave by the V wormhole at time 14, take part in the contest from time 15 to 21, and then use the W wormhole at time 21 to get back home. Therefore the time you have spent is (21 - 14 + 1) = 8. You can check that you cannot do better than this.
Input:
The first line contains 3 space separated integers N, X, and Y, where N is the number of contests, X is the number of time instances when wormhole V can be used and Y is the number of time instances when wormhole W can be used. The next N lines describe each contest. Each of these N lines contains two space separated integers S and E, where S is the starting time of the particular contest and E is the ending time of that contest, with S < E. The next line contains X space separated integers which are the time instances when the wormhole V can be used. The next line contains Y space separated integers which are the time instances when the wormhole W can be used.
Output:
Print a single line that contains a single integer, the minimum time needed to be spent to take part in a contest.
My code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <bits/stdc++.h>
#define loop(i,n) for(int i=0;i<n;++i)
#define FOR(i,a,b) for (int i = a; i < b; ++i)
#define ll long long
#define inf 9223372036854775807
#define printgrid(arr,m,n){cout<<"[";for(int i=0;i<m;++i){if(i!=0)cout<<" ";cout<<"[";for(int j=0;j<n;++j){cout<<arr[i][j];if(j!=n-1)cout<<" ";}cout<<"]";if(i!=m-1)cout<<endl;}cout<<"]"<<endl;}
using namespace std;
int searchl(int *arr,int s,int v)
{
int l=0;
int r=s-1;
while(l<=r)
{
int m = (l+r)/2;
if (arr[m] == v) return v;
if(arr[m]<v)
{
l = m + 1;
} else {
r = m - 1;
}
}
if (r<0) r=0;
return arr[r];
}
int searchh(int *arr,int s,int v)
{
int l=0;
int r=s-1;
while(l<=r)
{
int m = (l+r)/2;
if (arr[m] == v) return v;
if(arr[m]<v)
{
l = m + 1;
} else {
r = m - 1;
}
}
if(l>s-1) l=s-1;
return arr[l];
}
int main() {
int n,x,y;
cin>>n>>x>>y;
vector<pair<int,int>> v(n);
int a[x];
int b[y];
loop(i,n) cin >> v[i].first >> v[i].second;
loop(i,x) cin >> a[i];
loop(i,y) cin >> b[i];
sort(a,a+x);
sort(b,b+y);
int mn = 0x7fffffff;
int l;
int u;
loop(i,n)
{
l = searchl(a,x,v[i].first);
u = searchh(b,y,v[i].second);
mn = min(mn,(u-l+1));
}
cout << mn << endl;
}
What I tried is I use binary search for the lower range and upper range. and return the closest integer in the list. This should result in the lowest difference. This code AC's in 90% of the testcases however fails two testcases with a wrong answer.
Correct code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <bits/stdc++.h>
#define loop(i,n) for(int i=0;i<n;++i)
#define FOR(i,a,b) for (int i = a; i < b; ++i)
#define ll long long
#define inf 9223372036854775807
#define printgrid(arr,m,n){cout<<"[";for(int i=0;i<m;++i){if(i!=0)cout<<" ";cout<<"[";for(int j=0;j<n;++j){cout<<arr[i][j];if(j!=n-1)cout<<" ";}cout<<"]";if(i!=m-1)cout<<endl;}cout<<"]"<<endl;}
using namespace std;
int searchl(int *arr,int s,int v)
{
int l=0;
int r=s-1;
while(l<=r)
{
int m = (l+r)/2;
if (arr[m] == v) return v;
if(arr[m]<v)
{
l = m + 1;
} else {
r = m - 1;
}
}
if (r<0) return -1;
return arr[r];
}
int searchh(int *arr,int s,int v)
{
int l=0;
int r=s-1;
while(l<=r)
{
int m = (l+r)/2;
if (arr[m] == v) return v;
if(arr[m]<v)
{
l = m + 1;
} else {
r = m - 1;
}
}
if(l>s-1) return -1;
return arr[l];
}
int main() {
cin.sync_with_stdio(false);
cin.tie(NULL);
int n,x,y;
cin>>n>>x>>y;
vector<pair<int,int>> v(n);
int a[x];
int b[y];
loop(i,n) cin >> v[i].first >> v[i].second;
loop(i,x) cin >> a[i];
loop(i,y) cin >> b[i];
sort(a,a+x);
sort(b,b+y);
int mn = 0x7fffffff;
int l;
int u;
loop(i,n)
{
l = searchl(a,x,v[i].first);
u = searchh(b,y,v[i].second);
if(u==-1 or l==-1) continue;
mn = min(mn,(u-l+1));
}
cout << mn << endl;
}

Minimum cuts on a rectangle to make into squares

I'm trying to solve this problem:
Given an a×b rectangle, your task is to cut it into squares. On each move you can select a rectangle and cut it into two rectangles in such a way that all side lengths remain integers. What is the minimum possible number of moves?
My logic is that the minimum number of cuts means the minimum number of squares; I don't know if it's the correct approach.
I see which side is smaller, Now I know I need to cut bigSide/SmallSide of cuts to have squares of smallSide sides, then I am left with SmallSide and bigSide%smallSide. Then I go on till any side is 0 or both are equal.
#include <iostream>
int main() {
int a, b; std::cin >> a >> b; // sides of the rectangle
int res = 0;
while (a != 0 && b != 0) {
if (a > b) {
if (a % b == 0)
res += a / b - 1;
else
res += a / b;
a = a % b;
} else if (b > a) {
if (b % a == 0)
res += b / a - 1;
else
res += b / a;
b = b % a;
} else {
break;
}
}
std::cout << res;
return 0;
}
When the input is 404 288, my code gives 18, but the right answer is actually 10.
What am I doing wrong?
It seems clear to me that the problem defines each move as cutting a rectangle to two rectangles along the integer lines, and then asks for the minimum number of such cuts. As you can see there is a clear recursive nature in this problem. Once you cut a rectangle to two parts, you can recurse and cut each of them into squares with minimum moves and then sum up the answers. The problem is that the recursion might lead to exponential time complexity which leads us directly do dynamic programming. You have to use memoization to solve it efficiently (worst case time O(a*b*(a+b))) Here is what I'd suggest doing:
#include <iostream>
#include <vector>
using std::vector;
int min_cuts(int a, int b, vector<vector<int> > &mem) {
int min = mem[a][b];
// if already computed, just return the value
if (min > 0)
return min;
// if one side is divisible by the other,
// store min-cuts in 'min'
if (a%b==0)
min= a/b-1;
else if (b%a==0)
min= b/a -1;
// if there's no obvious solution, recurse
else {
// recurse on hight
for (int i=1; i<a/2; i++) {
int m = min_cuts(i,b, mem);
int n = min_cuts(a-i, b, mem);
if (min<0 or m+n+1<min)
min = m + n + 1;
}
// recurse on width
for (int j=1; j<b/2; j++) {
int m = min_cuts(a,j, mem);
int n = min_cuts(a, b-j, mem);
if (min<0 or m+n+1<min)
min = m + n + 1;
}
}
mem[a][b] = min;
return min;
}
int main() {
int a, b; std::cin >> a >> b; // sides of the rectangle
// -1 means the problem is not solved yet,
vector<vector<int> > mem(a+1, vector<int>(b+1, -1));
int res = min_cuts(a,b,mem);
std::cout << res << std::endl;
return 0;
}
The reason the foor loops go up until a/2 and b/2 is that cuting a paper is symmetric: if you cut along vertical line i it is the same as cutting along the line a-i if you flip the paper vertically. This is a little optimization hack that reduces complexity by a factor of 4 overall.
Another little hack is that by knowing that the problem is that if you transpose the paper the result is the same, meaining min_cuts(a,b)=min_cuts(b,a) you can potentially reduce computations by half. But any major further improvement, say a greedy algorithm would take more thinking (if there exists one at all).
The current answer is a good start, especially the suggestions to use memoization or dynamic programming, and potentially efficient enough.
Obviously, all answerers used the first with a sub-par data-structure. Vector-of-Vector has much space and performance overhead, using a (strict) lower triangular matrix stored in an array is much more efficient.
Using the maximum value as sentinel (easier with unsigned) would also reduce complexity.
Finally, let's move to dynamic programming instead of memoization to simplify and get even more efficient:
#include <algorithm>
#include <memory>
#include <utility>
constexpr unsigned min_cuts(unsigned a, unsigned b) {
if (a < b)
std::swap(a, b);
if (a == b || !b)
return 0;
const auto triangle = [](std::size_t n) { return n * (n - 1) / 2; };
const auto p = std::make_unique_for_overwrite<unsigned[]>(triangle(a));
/* const! */ unsigned zero = 0;
const auto f = [&](auto a, auto b) -> auto& {
if (a < b)
std::swap(a, b);
return a == b ? zero : p[triangle(a - 1) + b - 1];
};
for (auto i = 1u; i <= a; ++i) {
for (auto j = 1u; j < i; ++j) {
auto r = -1u;
for (auto k = i / 2; k; --k)
r = std::min(r, f(k, j) + f(i - k, j));
for (auto k = j / 2; k; --k)
r = std::min(r, f(k, i) + f(j - k, i));
f(i, j) = ++r;
}
}
return f(a, b);
}

Water pouring: Graph Implementation Problem - Shortest Path/Dijkstra's (?)

EDIT: Reposting; tried the problem a bit more and reposting. I'm genuinely lost at this point.
Have this small graph problem to do for today, was wondering if anyone had any possible solutions/insight for it.
Given two containers, one of which can accommodate a liters of water and the other b liters of water, determine the number of steps required to obtain exactly target liters of water in one of the containers, or -1 if it cannot be done.
At the beginning both containers are empty. The following operations are counted as 'steps':
Empty a contain er
Fill a container
Pour water from one container to the other, without spilling, until one of the containers is either full or empty
To get you started, we provide you a Node and a Graph class. Your job is to implement the function Graph createGraph(int capacity_a, int capacity_b), which builds a graph that contains all the possible container states given the capacity of two containers, and int findSolution(Graph g, int target), which does the graph traversal and returns the minimum steps.
You can modify the structures if you like, or build your own solution without using the provided structures. We will only test your int waterPouring(int a, int b, int target) function.
Hint: Which algorithm guarantees a minimun number of steps to reach a target?
My initial guess was Dijkstra's for the findSolution(), and attempted to translate some pseudo-code, but that didn't work out. I (think) I implemented createGraph correctly. Quite unsure as to where to go/if there's a better way to do this. Here's the code:
Thank you!
waterPouring.cpp:
#include <unordered_map>
#include <queue>
#include <vector>
using namespace std;
#define EMPTY 0
class Node {
public:
int a;
int b;
vector<Node *> neighbors;
Node () : a(EMPTY), b(EMPTY), neighbors() {}
Node (const int &a_, const int &b_) : a(a_), b(b_), neighbors() {}
Node (const int &a_, const int &b_, const vector<Node *> &neighbors_) : a(a_), b(b_), neighbors(neighbors_) {}
Node (const Node &tmpNode) : a(tmpNode.a), b(tmpNode.b), neighbors() {}
bool operator==(const Node & b_node)
{
return a == b_node.a && b == b_node.b;
}
Node &operator=(const Node & b_node) {
// WARNING: This operator does not copy the vector
a = b_node.a;
b = b_node.b;
return *this;
}
};
struct Graph {
vector<Node *> nodes;
};
Graph createGraph(int capacity_a, int capacity_b) {
// TODO
Graph g;
Node * capacityNode = new Node(capacity_a, capacity_b);
for (int i = 0; i < g.nodes.size(); i++) {
g.nodes.push_back(capacityNode);
}
return g;
}
int findSolution(Graph g, int target) {
// TODO: returns minimum number of steps to reach target liters of
water (or -1)
for (int& node : g) {
// not sure
}
return -1;
}
int waterPouring(int a, int b, int target) {
// Call createGraph
// Call findSolution
Graph stateMachineGraph = createGraph(a, b);
int steps = findSolution(stateMachineGraph, target);
for (Node *graphNode : stateMachineGraph.nodes)
{
delete graphNode;
}
return steps;
}
If you are ok with a solution without using a graph (provided task description allows it), you can do the following:
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
Suppose you have two containers with capacities a and b and you need to get c liters in the end.
First observe, that every operation you are allowed to perform moves x * a + y * b liters of water. E.g. if you are pouring water from the full second to the full first container you are pouring 1 * b - 1 * a. You can continue to persuade yourself that this is true. This gives us the following equation:
x * a + y * b = c
This is a diophantine equation and it has a solution if gcd(a, b) divides c (see Bézout's identity). You can solve it using extended Euclidean algorithm. If c is less than max(a, b) then either x or y less than zero. Suppose x > 0. Then you need to full a-container x times, pour water from it to b-container emptying it y times.
Example: a = 9, b = 5, c = 6. We have
-1 * 9 + 3 * 5 = 6
So, we need to
0 5 // Full the second (1)
5 0 // Pour to the first
5 5 // Full the second (2)
9 1 // Pour to the first
0 1 // Empty the first (-1)
1 0 // Pour to the first
1 5 // Full the second (3)
6 0 // Pour to the first
But if you really want to use graph, then
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <queue>
struct Node { int a, b; };
class Graph {
public:
std::vector<std::pair<Node, std::vector<int>>> nodes;
static Graph Create(int a, int b) {
auto index = [a,b](int i, int j) {
return i * (b + 1) + j;
};
Graph g;
for (int i = 0; i <= a; ++i) {
for (int j = 0; j <= b; ++j) {
std::vector<int> adj;
if (i < a) adj.push_back(index(a, j));
if (i > 0) adj.push_back(index(0, j));
if (j < b) adj.push_back(index(i, b));
if (j > 0) adj.push_back(index(i, 0));
int da = std::min(a - i, j);
int db = std::min(b - j, i);
adj.push_back(index(i + da, j - da));
adj.push_back(index(i - db, j + db));
std::sort(adj.begin(), adj.end());
adj.erase(std::unique(adj.begin(), adj.end()), adj.end());
g.nodes.push_back({ { i,j }, adj });
}
}
return g;
}
// Breadth-first search
std::pair<int, std::vector<int>> Shortest(int target) const {
std::vector<bool> visited(nodes.size(), 0);
std::vector<int> dist(nodes.size(), std::numeric_limits<int>::max());
std::vector<int> prev(nodes.size(), -1);
std::queue<int> q;
int cur_dist = 0;
q.push(0); visited[0] = true; dist[0] = 0;
while (q.size() > 0) {
int index = q.front(); q.pop();
for (auto i : nodes[index].second) {
if (nodes[i].first.a == target || nodes[i].first.b == target) {
int j = index;
std::vector<int> path = { i, index };
while (prev[j] != -1) {
path.push_back(j = prev[j]);
}
return { dist[index] + 1, path };
}
if (!visited[i]) {
q.push(i); visited[i] = true; dist[i] = dist[index] + 1; prev[i] = index;
}
}
}
return { -1, {} };
}
};
int main()
{
const auto g = Graph::Create(9, 5);
const auto p = g.Shortest(6);
for (int i = (int)p.second.size() - 1; i >= 0; --i) {
std::cout << g.nodes[p.second[i]].first.a << " " << g.nodes[p.second[i]].first.b << std::endl;
}
std::cout << std::endl << p.first << std::endl;
return 0;
}
The output is the same:
0 0
0 5
5 0
5 5
9 1
0 1
1 0
1 5
6 0
8