Codeforces 400D Dima and Bacteria(并查集最短路)

题意:有n个k种细菌,每种细菌有ci个,各自标号为
细菌之间(注意这里非种类之间)有m种转换关系,即u和v可相互转换,代价为x。
若每种细菌内部可两两转换且代价为0,输出Yes,并且用矩阵输入各种类之间最小转换代价,不能转换的输出-1。
否则输出No。
思路:
1、判断每种细菌内部可否两两转换且代价为0,可用并查集,对0边的两个细菌合并,最后判断每种细菌内部是否有共同祖先。
2、各种类之间最小转换代价则用Floyd。

 

ExpandedBlockStart.gif
  1 #include <cstdio>
  2 #include <cstring>
  3  #define N 100005
  4  #define M 505
  5  #define INF 99999999
  6 
  7  int fa[N], rank[N], vis[N];
  8  int a[M][M], c[M], up[M], down[M];
  9 
 10  void Floyd( int n) {
 11      for ( int k =  1; k <= n; k++) {
 12          for ( int i =  1; i <= n; i++) {
 13              for ( int j =  1; j <= n; j++) {
 14                  if((a[i][k] != INF) && (a[k][j] != INF)
 15                     &&(a[i][j] > a[i][k] + a[k][j] || a[i][j] == INF)) {
 16                         a[i][j] = a[i][k] + a[k][j];
 17                 }
 18             }
 19         }
 20     }
 21 }
 22 
 23 
 24  void Make_set( int n)
 25 {
 26      for( int i= 1; i<=n; i++)
 27     {
 28         fa[i] = i;
 29         rank[i] =  0;
 30     }
 31 }
 32  int find( int x) // 查找元素所在的父节点,回溯时压缩路径
 33  {
 34      if(x!=fa[x])
 35         fa[x]=find(fa[x]);
 36      return fa[x];
 37 }
 38  void Union( int x, int y) // 按秩合并x,y所在的集合
 39  {
 40     x=find(x);
 41     y=find(y);
 42      if(x==y)  return ;
 43      if(rank[x]>rank[y])
 44         fa[y]=x;
 45      else  if(rank[x]<rank[y])
 46         fa[x]=y;
 47      else {
 48         rank[x]++;
 49         fa[y]=x;
 50     }
 51 }
 52 
 53  int main()
 54 {
 55      int n, m, k;
 56     scanf( " %d%d%d ",&n,&m,&k);
 57     Make_set(n);
 58      for( int i= 1; i<=k; i++)
 59         scanf( " %d ",&c[i]);
 60      int tmp =  0;
 61      for( int i= 1; i<=k; i++)
 62     {
 63         up[i] = tmp +  1;
 64         down[i] = tmp + c[i];
 65         tmp += c[i];
 66          for( int j=up[i]; j<=down[i]; j++) vis[j] = i;
 67     }
 68      for( int i= 1; i<=k; i++)
 69          for( int j=i; j<=k; j++)
 70             a[i][j] = a[j][i] = INF;
 71 
 72      int x, y, d;
 73      for( int i= 1; i<=m; i++)
 74     {
 75         scanf( " %d%d%d ",&x,&y,&d);
 76          int ta = vis[x], tb = vis[y];
 77          if(d== 0// 0边均
 78              Union(x, y);
 79          if(ta!=tb)
 80         {
 81              if(d<a[ta][tb])
 82                 a[ta][tb] = a[tb][ta] = d;
 83 
 84         }
 85     }
 86 
 87      int flag =  0;
 88      for( int i= 1; i<=k; i++)
 89     {
 90          int flag1 =  0;
 91          int tmp = find(up[i]);
 92          for( int j=up[i]+ 1; j<=down[i]; j++)
 93         {
 94              if(find(j)!=tmp)
 95             {
 96                 flag1 =  1;
 97                  break;
 98             }
 99         }
100          if(flag1 ==  1)
101         {
102             flag= 1;
103              break;
104         }
105     }
106      if(flag== 0)
107     {
108         Floyd(k);
109         printf( " Yes\n ");
110          for( int i= 1; i<=k; i++)
111         {
112              for( int j= 1; j<=k; j++)
113             {
114                  if(i==j) printf( " ");
115                  else  if(a[i][j]==INF) printf( " -1  ");
116                  else
117                     printf( " %d  ",a[i][j]);
118             }
119             printf( " \n ");
120         }
121     }
122      else
123         printf( " No\n ");
124 
125      return  0;
126 }
View Code

转载于:https://www.cnblogs.com/byluoluo/p/3616592.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值