codeforces472D
Design Tutorial: Inverse the Problem
给你了一个 n × n最短距离矩阵。(即矩阵中dis[u][v]为u点到v点的最短距离),判断是否存在一个边权皆为正整数的树,恰好满足这个最短距离矩阵 。
Input
第一行为一个整数 n (1 ≤ n ≤ 2000) — 表示图中有多少个点.
下面 n 行,每行包括 n 个整数 di, j (0 ≤ di, j ≤ 109) — 点i和点j之间的最短距离.
Output
如果存在这样的树,输出 "YES", 否则输出"NO".
Examples
Input
3
0 2 7
2 0 9
7 9 0
Output
YES
Input
3
1 2 7
2 0 9
7 9 0
Output
NO
Input
3
0 2 2
7 0 9
7 9 0
Output
NO
Input
3
0 1 1
1 0 1
1 1 0
Output
NO
Input
2
0 0
0 0
Output
NO
sol:首先很明显的性质就是在树上,两个点之间一定有且仅有一条最短的路径,于是很明显用Kruskal把MST构建出来之后暴力dfs判断距离是否相等即可
Ps:根据样例可以特判掉很多奇奇怪怪的情况,比方说Dis[x][x]!=0或者Dis[x][y]!=Dis[y][x]等等(不过对答案毫无影响)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=2005,M=4000005; int n,Dis[N][N]; namespace Tree { int tot=0,Next[M],to[M],val[M],head[N]; int cnt=0; struct Edge { int U,V,Quan; inline bool operator<(const Edge &tmp)const { return Quan<tmp.Quan; } }E[M]; inline void add(int x,int y,int z) { Next[++tot]=head[x]; to[tot]=y; val[tot]=z; head[x]=tot; } int Father[N]; inline int GetFa(int x) { return (Father[x]==x)?(Father[x]):(Father[x]=GetFa(Father[x])); } int Root,Path[N][N]; inline void dfs(int x,int fa,int Sum) { int i; Path[Root][x]=Sum; for(i=head[x];i;i=Next[i]) if(to[i]!=fa) { dfs(to[i],x,Sum+val[i]); } } inline void Solve() { int i,j; for(i=1;i<=n;i++) { Father[i]=i; for(j=1;j<i;j++) E[++cnt]=(Edge){j,i,Dis[j][i]}; } sort(E+1,E+cnt+1); for(i=1;i<=cnt;i++) { Edge tmp=E[i]; int x=tmp.U,y=tmp.V,z=tmp.Quan; int xx=GetFa(x),yy=GetFa(y); if(xx==yy) continue; Father[xx]=yy; add(x,y,z); add(y,x,z); } for(i=1;i<=n;i++) Root=i,dfs(i,0,0); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) if(Path[i][j]!=Dis[i][j]) { puts("NO"); exit(0); } } puts("YES"); } } int main() { int i,j; R(n); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) R(Dis[i][j]); } for(i=1;i<=n;i++) { if(Dis[i][i]) return puts("NO"),0; for(j=1;j<i;j++) if((Dis[j][i]!=Dis[i][j])||(!Dis[j][i])) return puts("NO"),0; } Tree::Solve(); return 0; } /* Input 3 0 2 7 2 0 9 7 9 0 Output YES Input 3 1 2 7 2 0 9 7 9 0 Output NO Input 3 0 2 2 7 0 9 7 9 0 Output NO Input 3 0 1 1 1 0 1 1 1 0 Output NO Input 2 0 0 0 0 Output NO */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!