Design Tutorial: Inverse the Problem

CodeForces - 472D

给你了一个 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]等等(不过对答案毫无影响)
#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
ll s=;
bool f=;
char ch=' ';
while(!isdigit(ch))
{
f|=(ch=='-'); ch=getchar();
}
while(isdigit(ch))
{
s=(s<<)+(s<<)+(ch^); ch=getchar();
}
return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
if(x<)
{
putchar('-'); x=-x;
}
if(x<)
{
putchar(x+''); return;
}
write(x/);
putchar((x%)+'');
return;
}
#define W(x) write(x),putchar(' ')
#define Wl(x) write(x),putchar('\n')
const int N=,M=;
int n,Dis[N][N];
namespace Tree
{
int tot=,Next[M],to[M],val[M],head[N];
int cnt=;
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=;i<=n;i++)
{
Father[i]=i;
for(j=;j<i;j++) E[++cnt]=(Edge){j,i,Dis[j][i]};
}
sort(E+,E+cnt+);
for(i=;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=;i<=n;i++) Root=i,dfs(i,,);
for(i=;i<=n;i++)
{
for(j=;j<=n;j++) if(Path[i][j]!=Dis[i][j])
{
puts("NO"); exit();
}
}
puts("YES");
}
}
int main()
{
int i,j;
R(n);
for(i=;i<=n;i++)
{
for(j=;j<=n;j++) R(Dis[i][j]);
}
for(i=;i<=n;i++)
{
if(Dis[i][i]) return puts("NO"),;
for(j=;j<i;j++) if((Dis[j][i]!=Dis[i][j])||(!Dis[j][i])) return puts("NO"),;
}
Tree::Solve();
return ;
}
/*
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
*/
 

codeforces472D的更多相关文章

随机推荐

  1. DevExpress13.2.9 控件使用经验总结

    一.换肤功能 1. 添加 DevExpress.OfficeSkins 和 DevExpress.BonusSkins 两个引用 2. 皮肤注册 DevExpress.UserSkins.BonusS ...

  2. Struts 类型转换之局部和全局配置

    我们碰到过很多情况,就是时间日期常常会出现错误,这是我们最头疼的事,在struts2中有一些内置转换器,也有一些需要我们自己配置. 我们为什么需要类型转换呢? 在基于HTTP协议的Web应用中 客户端 ...

  3. HTML基本结构及标签样式

    <!DOCTYPE html>————声明 <html> <head>————头部设置信息 <title>文件标题</title> < ...

  4. MD5计算器

    private void radioBtnFlie_CheckedChanged(object sender, EventArgs e) { RadioButton rb = sender as Ra ...

  5. js 捕捉回车键触发登录,并验证输入内容

    js 捕捉回车键触发登录,并验证输入内容 有时候我们会遇到 web 页面中捕捉按键,触发一些效果, 比如常见的回车键触发登录,并验证输入内容,下面会介绍,截图: 一.最简单的捕捉回车键:判断按下的是不 ...

  6. Java基础之StringBuffer和StringBuilder的区别

    StringBuffer是一个字符串的缓存类,属于一个容器,对于容器,我们可以进行增删改查. StringBuffer的容器长度是可变的,并且里面可以存放多种的数据类型.它跟其他容器,比如数组,是很不 ...

  7. css3圆形光环闪烁效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. SQL查询一个表中类别字段中Max()最大值对应的记录

      SQL查询一个表中类别字段中Max()最大值对应的记录 SELECT A.id, A.name, A.version FROM   DOC A, (SELECT id, MAX(version)  ...

  9. 进程间通信——队列和管道(multiprocess.Queue、multiprocess.Pipe)

    进程: 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运 ...

  10. 打通版微社区(6):部署微信插件及开通QQ云服务

    写在前面: 此文是我最后写的.其实实际部署的时候,我是先安装了论坛并试图开通微信的微社区.发现微社区需要在微信公众平台的开发者中心里配置 "网页账号,网页授权获取用户基本信息"为论 ...