472D - Design Tutorial: Inverse The Problem
Solution:
Pretty interesting problem. Given a matrix of distances between nodes dist[u][v], return a weighted tree that satisfies this matrix. Here is my approach (I believe this is not the most efficient (or even efficient) or clever idea, but it worked :P)
First, based on the distances, run Dijkstra algorithm choosing any node as the source, and we will have a shortest path tree with as that source node as the root. We will also have the information of the shortest distances from root to all other nodes, d[u]. Then the idea is, for each pair of node, we check whether the distance matrix is consistent with this information on shortest distances from root. For each node u and v, we have two cases:
1. The lowest common ancestor of u and v is u or v themselves. Then if the tree is valid, dist[u][v] = |d[u] - d[v]|.
2. The lowest common ancestor of u and v is another node p. Then p is the root of a subtree containing u and v, hence we must have dist[u][v] = d[u] + d[v] - 2 * d[p].
If all these are consistent, then we have found the desired tree. Finding lowest common ancestor can be done in \(O(N\lg{N})\) using segment tree on the euler path of the shortest path tree.
Implementation:
#include <iostream> #include <cstdio> #include <vector> #include <utility> #include <algorithm> #include <cstring> using namespace std; long long INF = (long long) 1e10; int vis[2003]; int dist[2003][2003]; int idx[2003]; int height[2003]; int segtree[16003]; int head[2003]; long long d[2003]; vector<int> euler; vector<vector<int> > tree; int n; bool ok = true;
void djikstra(int source){ for(int i=0;i<n;++i){ d[i] = INF; } d[source] = 0; head[source] = source; int cnt = 0; while(cnt != n){ int cur = -1; for(int i=0;i<n;++i){ if(vis[i]) continue; if(cur == -1 || d[cur] > d[i]){ cur = i; } } if(d[cur] == 0 && cur != source) { ok = false; break; } vis[cur] = 1; if(head[cur] != cur) { tree[head[cur]].push_back(cur); tree[cur].push_back(head[cur]); } for(int i=0;i<n;++i){ if(vis[i]) continue; if(d[i] >= d[cur] + dist[cur][i]){ d[i] = d[cur] + dist[cur][i]; head[i] = cur; } } ++cnt; } } void dfs(int u, int level) { vis[u] = 1; euler.push_back(u); idx[u] = euler.size()-1; height[u] = level; for(int i=0;i<tree[u].size();++i){ int v = tree[u][i]; if(vis[v]) continue; dfs(v, level+1); euler.push_back(u); } } void build(int p, int L, int R){ if(L==R){ segtree[p] = euler[L]; return; } int M = (L+R)/2; build(2*p, L, M); build(2*p+1, M+1, R); segtree[p] = (height[segtree[2*p]] < height[segtree[2*p+1]] ? segtree[2*p] : segtree[2*p+1]); } int rmq(int p, int L, int R, int S, int T){ if(R < S || T < L) return -1; if(S <= L && R <= T) { return segtree[p]; } int M = (L+R)/2; int left = rmq(2*p, L, M, S, T); int right = rmq(2*p+1, M+1, R, S, T); if(left == -1) return right; if(right == -1) return left; return (height[left] < height[right] ? left : right); } int main(){ scanf("%d", &n); for(int i=0;i<n;++i){ for(int j=0;j<n;++j){ scanf("%d", &dist[i][j]); } } tree = vector<vector<int> > (n+3); d[0] = 0; djikstra(0); if(!ok){ printf("NO\n"); return 0; } memset(vis, 0, sizeof vis); dfs(0,0); build(1,0,euler.size()-1); for(int i=0;i<n;++i){ for(int j=0;j<n;++j){ if(idx[j] < idx[i]) continue; int par = rmq(1, 0, euler.size()-1, idx[i], idx[j]); if(i == par || par == j || i == j) { int tmp = max(d[i]-d[j], d[j] - d[i]); if(dist[i][j] != tmp || dist[i][j] != dist[j][i]){ ok = false; break; } } else { if(dist[i][j] != d[i] + d[j] - 2*d[par] || dist[j][i] != dist[j][i]) { ok = false; break; } } } if(!ok)break; } if(ok) printf("YES\n"); else printf("NO\n"); return 0; }
No comments:
Post a Comment