藏宝图

题目描述

Czy爬上黑红树,到达了一个奇怪的地方……

Czy发现了一张奇怪的藏宝图。图上有n个点,m条无向边。已经标出了图中两两之间距离dist。但是czy知道,只有当图刚好又是一颗树的时候,这张藏宝图才是真的。如果藏宝图是真的,那么经过点x的边的边权平均数最大的那个x是藏着宝物的地方。请计算这是不是真的藏宝图,如果是真的藏宝之处在哪里。

 

输入

输入数据第一行一个数T,表示T组数据。

对于每组数据,第一行一个n,表示藏宝图上的点的个数。

接下来n行,每行n个数,表示两两节点之间的距离。

 

输出

输出一行或两行。第一行”Yes”或”No”,表示这是不是真的藏宝图。

若是真的藏宝图,第二行再输出一个数,表示哪个点是藏宝之处。

 

样例输入

2
3
0 7 9
7 0 2
9 2 0
3
0 2 7
2 0 9
7 9 0

样例输出

Yes
1
Yes
3

样例解释:第一棵树的形状是1--2--3。1、2之间的边权是7,2、3之间是2。
 第二棵树的形状是2--1--3。2、1之间的边权是2,1、3之间是7。

提示

对于30%数据,n<=50,1<=树上的边的长度<=10^9。

对于50%数据,n<=600.

对于100%数据,1<=n<=2500,除30%小数据外任意0<=dist[i][j]<=10^9,T<=5

【题解】

        考试的时候完全没有思路,想试试把多余边都拆掉能不能行,但是完全没有想起“最小生成树”这几个字眼。其实想想最小生成树,加有用的边不就相当于删无用的边吗?还是得懂得变通啊。

       稠密图卡克鲁斯卡尔是众所周知,而每两点之间都有边可算得上是最稠密不过的图了,复习普里姆,选择最近的点把它加入树。建出树之后dfs求各点到1的距离,lca求各两点之间距离,验证一下是否是树。是树不是树都好办,这题主要是步骤非常多,用了很多算法,调试有一定难度(我也不出意料地打得非常冗长)。思路上大概也只有最小生成树和稠密图比较困难吧。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int sj=2505;
ll ca,n,dist[sj][sj],dis[sj],mi;
int e,xb[sj],lca[sj][sj],f[sj],zx,h[sj],fa[sj];
bool r[sj],op;
void init()
{
     scanf("%d",&n);
     memset(dis,0x3f,sizeof(dis));
     memset(f,-1,sizeof(f));
     memset(h,-1,sizeof(h));
     memset(r,0,sizeof(r));
     for(int i=1;i<=n;i++)
     {
       for(int j=1;j<=n;j++)
         scanf("%lld",&dist[i][j]);
     }
}
struct B
{
     int ne,v,w;
}b[sj*2];
void add(int x,int y,int z)
{
     b[e].v=y;
     b[e].ne=h[x];
     b[e].w=z;
     h[x]=e++;
}
void mst()
{
     memset(dis,0x3f,sizeof(dis));
     for(int i=1;i<=n;i++)
       if(dist[i][1]<dis[i])
       {
         dis[i]=dist[i][1];
         fa[i]=1;
       }
     r[1]=1;
     for(int i=1;i<=n-1;i++)
     {
        mi=0x7fffffff;
        zx=1;
        for(int j=1;j<=n;j++)
          if(!r[j]&&dis[j]<mi)
            mi=dis[j],zx=j;
        r[zx]=1;
        add(fa[zx],zx,dis[zx]);
        add(zx,fa[zx],dis[zx]);
        for(int j=1;j<=n;j++)
          if(!r[j]&&dist[zx][j]<dis[j])
          { 
            dis[j]=dist[zx][j];
            fa[j]=zx;
          }
     }
}
int find(int x)
{
     if(f[x]==-1) return x;
     f[x]=find(f[x]);
     return f[x];
}
void hb(int x,int y)
{
     x=find(x);
     y=find(y);
     if(x!=y)
       f[x]=y;
}
void dfs(int x)
{
     r[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
       if(!r[b[i].v]&&b[i].v!=fa[x])
       {
         dis[b[i].v]=dis[x]+b[i].w;
         dfs(b[i].v);
       }
}
void tarjan(int x)
{
     xb[x]=x;
     r[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
       if(!r[b[i].v])
       {
          tarjan(b[i].v);
          hb(x,b[i].v);
          xb[find(b[i].v)]=x;
       }
     for(int i=1;i<=n;i++)
       if(r[i])
         lca[i][x]=lca[x][i]=xb[find(i)];
}
int main()
{
    //freopen("t3.txt","r",stdin);
    //freopen("treas9.in","r",stdin);
    //freopen("treas.out","w",stdout);
    scanf("%d",&ca);
    for(int l=1;l<=ca;l++)
    {
      init();
      mst();
      memset(r,0,sizeof(r));
      dfs(1);
      memset(r,0,sizeof(r));
      tarjan(1);
      op=1;
      for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
          if(dis[i]+dis[j]-2*dis[lca[i][j]]!=dist[i][j])
          {
             op=0;
             break;
          }
      if(!op) printf("No\n");
      if(op)
      {
          printf("Yes\n");
          double temp;
          int ge;
          mi=0;
          zx=1;
          for(int i=1;i<=n;i++)
          {
            ge=0;
            temp=0;
            for(int j=h[i];j!=-1;j=b[j].ne)
            {
              temp+=abs(dis[i]-dis[b[j].v]);
              ge++;
            }
            temp/=ge;
            if(temp>mi)
            {
               mi=temp;
               zx=i;
            }
          }
          printf("%d\n",zx);
      }
    }
    //while(1);
    return 0;
}

 

 

 

posted @ 2017-07-28 21:30  moyiii  阅读(250)  评论(0编辑  收藏  举报