南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路。
现在已经知道哪些城市之间可以修路,如果修路,花费是多少。
现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少。
但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧。
2 3 3 1 2 1 2 3 2 3 1 3 4 4 1 2 2 2 3 2 3 4 2 4 1 2
No Yes
对于最小生成树(可以用kruskal和prime算法求得,在这里我是用kruskal求得,如果不会请自己百度。),边的权值的和最小称为最小生成树。
而次小生成树就是除了最小生成树外的最小生成树。而且所有的次小生成树都是通过最小生成树的换边得到的。
所以难点就是如何换边。
对于如何换边:
1.先求出最小生成树,值为x。
2.一一枚举添加不在生成树上的边(这时候一定形成了一个环)
3.寻找环上的(最小生成树上的边)权值最大值与你所添加不在生成树上的边的权值比较,所得到的差值为min。
由于是一一枚举添加边,min有多个,求出最小的哪一个,所以次小生成树就为x+min。
所以对于这道题也就很简单了,只要min=0就代表有多种方案了。具体请看代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; struct node { int a,b,c,vis; }c[200005]; int fa[505],v,e,stamp[505][505],visit[505]; bool cmp(node a,node b) { return a.c<b.c; } int find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } void init() { for(int i=1;i<=v;i++) fa[i]=i; } void comb(int x1,int x2,int cost)//存贮最小生成树的边 { stamp[x1][x2]=stamp[x2][x1]=cost; } int sec_find(int x,int y,int min)//换边 { if(x==y) return min; for(int i=1;i<=v;i++) { if(stamp[x][i]&&!visit[i]) { visit[x]=1; if(stamp[x][i]>min) min=stamp[x][i]; sec_find(i,y,min); } } return min; } int main() { int ncase,mintree,sec; scanf("%d",&ncase); while(ncase--) { memset(stamp,0,sizeof(stamp)); memset(visit,0,sizeof(visit)); scanf("%d %d",&v,&e); init(); for(int i=0;i<e;i++) scanf("%d %d %d",&c[i].a,&c[i].b,&c[i].c); sort(c,c+e,cmp); int mintree=0; for(int i=0;i<e;i++)//求最小生成树 { int x1=find(c[i].a); int x2=find(c[i].b); if(x1!=x2) fa[x1]=x2,mintree+=c[i].c,c[i].vis=1,comb(x1,x2,c[i].c); else c[i].vis=0;//对于不在最小生成树里面的边标记一下 } int flag=0; for(int i=0;i<e;i++) if(!c[i].vis)//最小生成树之外的边 { sec=sec_find(c[i].a,c[i].b,-1000000); if(sec==c[i].c) { flag=1; break; } } if(flag) printf("Yes\n"); else printf("No\n"); } return 0; }