CF472D Design Tutorial: Inverse the Problem
感谢所有AC
传送门
思路
先根据所给的邻接矩阵建图。题目要验证是否能形成一棵树,首先这棵树一定是这个图的一个生成树,其次,树中是不存在环的,所以生成树中不能带环。假如有三个点形成了一个环,边权分别为 x,y,z ,同时有 x <= y < z ,那么可以怎么验证树的存在性呢?只要 x+y=z 等式成立,这个树就能构成,当然,这是对于三个点构成的树而言。对于包含n个点的数,只要验证任意三个点的可行性就行。
考虑怎么代码实现,先把 x 和 y 这两条边加入生成树中,然后对 z 对应的两个点求距离,看看他们在树上的距离是否等于 z ,如果等,则树成立。这个过程少不了对 x,y,z 先进行排序,然后取最短边,取次短边来建树。从这可以发现这个过程分明是求最小生成树的步骤。于是对原图求最小生成树,求树上距离来验证树的存在性。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 2007
using namespace std;
int n, d[maxn][maxn], hd[maxn], dis[maxn], fa[maxn], cnt, m;
struct node {
int u, v, w;
bool operator<(const node& a)const {
return w < a.w;
}
}edge[maxn*maxn];
struct edgenode {
int to, val, nxt;
}g[maxn*maxn*2];
int find(int x)
{
if (x != fa[x])
return fa[x] = find(fa[x]);
return x;
}
void add(int u, int v, int w)
{
g[++cnt].val = w;
g[cnt].to = v;
g[cnt].nxt = hd[u];
hd[u] = cnt;
}
void dfs(int r, int u)
{
for (int i = hd[u]; i; i = g[i].nxt)
{
int v = g[i].to, w = g[i].val;
if (v == r) continue;
dis[v] = dis[u] + w;
dfs(u, v);
}
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
cin >> d[i][j];
if (i == j && d[i][j] != 0) { cout << "NO"; return 0; }
if (i != j && d[i][j] <= 0) { cout << "NO"; return 0; }
if (i < j)
edge[++m].u = i, edge[m].v = j, edge[m].w = d[i][j];
}
sort(edge + 1, edge + m + 1);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++)
{
int fu = find(edge[i].u), fv = find(edge[i].v);
if (fu == fv) continue;
fa[fu] = fv;
add(edge[i].u, edge[i].v, edge[i].w);
add(edge[i].v, edge[i].u, edge[i].w);
}
for (int i = 1; i <= n; i++)
{
dis[i] = 0;
dfs(0, i);
for(int j=1;j<=n;j++)
if (dis[j] != d[i][j])
{
cout << "NO";
return 0;
}
}
cout << "YES";
return 0;
}