POJ 2831 Can We Build This One?(最小生成树)

题目链接:http://poj.org/problem?id=2831 

题意: 

题目大意:给张图,然后问你,如果某边的权值下降为V,那么这个边有无可能在最小生成树中呢?节点数≤1000,边数≤100000,询问数≤100000。

 

 思路:prim。

考虑prim的算法过程:每次加入一个点,并且加入该点的条件是dis[i]是还没加入点的dis[]中最小的。

所以执行prim时,用数组no[]保存某点加入的次序,numm[i]保存第i个加入的点的id

查询边(a,b)时(假设a的加入次序先于b),则枚举no[a]+1 ~ no[b]加入的点的dis[],若存在x<=dis[k],原prim算法过程可被打破,边(a,b)会被选择,

因为该边是当前最佳的边;否则还是原算法过程进行。 

 

ExpandedBlockStart.gif
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <cstring>
 5 #include <iomanip>
 6  using  namespace std;
 7 
 8  const  int maxd =  1000001;
 9  const  int N =  1005;
10  const  int M =  1000001;
11 
12  int map1[N][N], dis[N], vis[N], no[N], numm[N];
13  struct Path
14 {
15      int a, b, c;
16 }p[M];
17 
18  void prim( int n)
19 {
20      int cur;
21      for( int i= 1; i<=n; i++) dis[i] = maxd;
22     memset(vis,  0sizeof(vis));
23     cur =  1; dis[cur] =  0; vis[cur] =  1;
24     no[cur] =  1; numm[ 1] = cur;
25      // 找n-1轮
26       for( int i= 2; i<=n; i++)
27     {
28          // 枚举上一次加入的点与各个点的距离,更新最小距离dis[i]
29           for( int j= 1; j<=n; j++)
30         {
31              if(vis[j]== 0 && dis[j]>map1[cur][j])
32                 dis[j] = map1[cur][j];
33         }
34          // 选出最短的边,把该边连接的点加入结果集
35           int mind = maxd;
36          for( int j= 1; j<=n; j++)
37         {
38              if(vis[j]== 0 && dis[j]<mind)
39             {
40                 mind = dis[j];
41                 cur = j;
42             }
43         }
44         vis[cur] =  1;
45         no[cur] = i;
46         numm[i] = cur;
47     }
48 }
49 
50  void init( int n)
51 {
52      for( int i= 1; i<=n; i++)
53          for( int j= 1; j<=n; j++)
54             map1[i][j] = maxd;
55 }
56 
57  int main()
58 {
59      int n, m, q, pi, x;
60     scanf( " %d%d%d ",&n, &m, &q);
61     init(n);
62      for( int i= 1; i<=m; i++)
63     {
64         scanf( " %d%d%d ",&p[i].a,&p[i].b,&p[i].c);
65          if(map1[p[i].a][p[i].b]>p[i].c)
66             map1[p[i].a][p[i].b] = map1[p[i].b][p[i].a] = p[i].c;
67     }
68      // prim走一遍,保存no[],numm[],dis[]
69      prim(n);
70 
71      for( int i= 1; i<=q; i++)
72     {
73         scanf( " %d%d ",&pi,&x);
74          int a = no[p[pi].a], b = no[p[pi].b], temp, flag =  0, bno, bnum, sno, snum;
75          if(a>b){bno = a; bnum = p[pi].a; sno = b; snum = p[pi].b;}
76          else {bno = b; bnum = p[pi].b; sno = a; snum = p[pi].a;}
77          // 枚举p[pi].a 和 p[pi].b两点之间加入的点
78           // 若x<=dis[],则该边为这轮的最优边,可成为最小生成树中的一边
79           for( int k=sno+ 1; k<=bno; k++)
80         {
81              if(x<=dis[numm[k]])
82             {
83                 flag =  1;
84                  break;
85             }
86         }
87          if(flag== 1)
88             printf( " Yes\n ");
89          else
90             printf( " No\n ");
91     }
92      return  0;
93 }
View Code 

 

 

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值