【POJ 1679 The Unique MST】最小生成树

简介: 无向连通图(无重边),判断最小生成树是否唯一,若唯一求边权和。 分析生成树的生成过程,只有一个圈内出现权值相同的边才会出现权值和相等但“异构”的生成树。(并不一定是最小生成树) 分析贪心策略求最小生成树的过程(贪心地选最短的边来扩充已加入生成树的顶点集合U),发现只有当出现“U中两个不同的点到V-U中同一点的距离同时为当前最短边”时,才会出现“异构”的最小生成树。

无向连通图(无重边),判断最小生成树是否唯一,若唯一求边权和。

分析生成树的生成过程,只有一个圈内出现权值相同的边才会出现权值和相等但“异构”的生成树。(并不一定是最小生成树)

分析贪心策略求最小生成树的过程(贪心地选最短的边来扩充已加入生成树的顶点集合U),发现只有当出现“U中两个不同的点到V-U中同一点的距离同时为当前最短边”时,才会出现“异构”的最小生成树。

上图为kruscal和prim生成过程中可能遇到的相等边的情况,红色和蓝色的为权值相等的边。

可以看到kruscal由于事先将所有边按权值排序,所以在构造MST的过程中,当发现权值相同的边时,需要遍历之前遇到过的所有相同权值的边来判断是否发生“争抢同一点”的情况,若发现即可判定存在“异构”最小生成树。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int MAX_N=105;
 6 const int MAX_M=5005;
 7 
 8 int par[MAX_N];
 9 void init()
10 {
11     memset(par,-1,sizeof(par));
12 }
13 int find(int x)
14 {
15     if(par[x]==-1) return x;
16     return par[x]=find(par[x]);
17 }
18 void unite(int x,int y)
19 {
20     x=find(x);
21     y=find(y);
22     if(x!=y) par[x]=y;
23 }
24 bool same(int x,int y)
25 {
26     return find(x)==find(y);
27 }
28 
29 int t;
30 int n,m;
31 struct Edge
32 {
33     int u,v,w;
34 }e[MAX_M];
35 
36 bool cmp(Edge e1,Edge e2)
37 {
38     return e1.w<e2.w;
39 }
40 
41 bool inter(Edge e1,Edge e2)
42 {
43     if(e1.u==e2.u||e1.u==e2.v||e1.v==e2.u||e1.v==e2.v) return true;
44     else return false;
45 }
46 
47 int kruscal()
48 {
49     int ans=0;
50     init();
51     ans+=e[0].w;
52     unite(e[0].u,e[0].v);
53     int cur_w=e[0].w;
54     for(int i=1;i<m;i++)
55     {
56         if(same(e[i].u,e[i].v))
57         {
58             for(int j=i-1;e[j].w==e[i].w;j--)
59             {
60                 if(inter(e[i],e[j]))//判两条边有无交点
61                 {
62                     ans=-1;
63                     break;
64                 }
65             }
66         }else
67         {
68             unite(e[i].u,e[i].v);
69             ans+=e[i].w;
70             cur_w=e[i].w;
71         }
72     }
73     return ans;
74 }
75 
76 int main()
77 {
78     freopen("1679.txt","r",stdin);
79     scanf("%d",&t);
80     while(t--)
81     {
82         scanf("%d%d",&n,&m);
83         for(int i=0;i<m;i++)
84         {
85             scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
86         }
87         if(n==1) {printf("0\n"); continue;}
88         sort(e,e+m,cmp);
89         int ans=kruscal();
90         if(ans==-1)
91             printf("Not Unique!\n");
92         else printf("%d\n",ans);
93     }
94     return 0;
95 }
kruscal

 

而prim由于每次都选连结U和V-U的边,所以不会遇到kruscal那样蓝色的可能误判的边(我开始就WA在这里),因此只需在加入一个新点更新其他点的mincost时判断有没有和mincost值相等的另一条边的即可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <vector>
 4 #include <queue>
 5 using namespace std;
 6 const int MAX_N=105;
 7 const int MAX_M=5005;
 8 const int INF=100000;
 9 typedef pair<int,int> P;//cost,to
10 struct Edge
11 {
12     int to,cost;
13     Edge(){}
14     Edge(int tt,int cc):to(tt),cost(cc){}
15 };
16 int t;
17 int n,m;
18 vector<Edge> G[MAX_N];
19 int vis[MAX_N];
20 int mincost[MAX_N];
21 
22 int prim()
23 {
24     int ans=0;
25     int flag=0;
26     priority_queue<P,vector<P>,greater<P> > que;
27     memset(vis,0,sizeof(vis));
28     for(int i=1;i<=n;i++) mincost[i]=INF;
29     que.push(P(0,1));
30     mincost[1]=0;
31     while(!que.empty())
32     {
33         P p=que.top();
34         que.pop();
35         int v=p.second;
36         if(vis[v]||mincost[v]<p.first) continue;
37         vis[v]=1;
38         mincost[v]=p.first;
39         ans+=mincost[v];
40         for(int i=0;i<G[v].size();i++)
41         {
42             int u=G[v][i].to;
43             if(!vis[u])
44             {
45                 if(mincost[u]>G[v][i].cost&&G[v][i].cost<INF)
46                 {
47                     mincost[u]=G[v][i].cost;
48                     que.push(P(mincost[u],u));
49                 }else if(mincost[u]==G[v][i].cost)//存在到达点u权值相等且都为最小值的边
50                 {
51                     flag=1;
52                     break;
53                 }
54             }
55         }
56         if(flag) break;
57     }
58     if(flag) ans=-1;
59     return ans;
60 }
61 int main()
62 {
63     freopen("1679.txt","r",stdin);
64     scanf("%d",&t);
65     while(t--)
66     {
67         scanf("%d%d",&n,&m);
68         for(int i=1;i<=n;i++) G[i].clear();
69         for(int i=0;i<m;i++)
70         {
71             int u,v,w;
72             scanf("%d%d%d",&u,&v,&w);
73             G[u].push_back(Edge(v,w));
74             G[v].push_back(Edge(u,w));
75         }
76         if(n==1) {printf("0\n"); continue;}
77         int ans=prim();
78         if(ans==-1)
79             printf("Not Unique!\n");
80         else printf("%d\n",ans);
81     }
82     return 0;
83 }
prim_priorityqueue

 

目录
相关文章
|
6月前
|
存储
poj 3254 Corn Fields (状态压缩dp)
状态压缩dp其实就是用二进制来表示所有的状态,比如这题, 我们在某一行可以这样取0 1 0 1 1 0 1,用1代表取了,0代表没取,因为这点,它的数据量也限制在20以内,所有看到这样数据量的题目可以先考虑一下状态压缩dp。对于有多行的数据,所有状态的总数必然很庞大,而且不用特殊的方法想要存储这些状态是不太现实的。既然每个点只有这两种情况,我们可以用二进制的一位来表示,0 1 0 1 1 0 1就可以表示为二进制0101101也就是十进制的45,如果我们想要枚举6个点的所有状态,我们只需要从0到2^6取其二进制就可以了,并不会重复或是多余。
18 0
UPC Graph (最小生成树 || 并查集+二分)
UPC Graph (最小生成树 || 并查集+二分)
68 0