http://poj.org/problem?id=1679
题目大意:
给你一些点,判断MST(最小生成树)是否唯一。
思路:
以前做过这题,不过写的是O(n^3)的,今天学了一招O(n^2)的,哈哈~
方法一:
首先先建立MST,然后把这个MST的边一个个尝试不使用,构建另外一颗MST,然后判断权值是否相等。
这样复杂度需要O(n^3)。。
方法二:
还可以用次最小生成树的方法解决:如果最小生成树不唯一,那么次小生成树的权值和最小生成树相同。
我们可以枚举要加入哪一条新边。在最小生成树上加一条边u-v之后,图上会出现一条回路。因此删除的边必须在最小生成树u到v的路径上,而且是这条路径上的最长边。而次最小生成树一定能由最小生成树加一条再删除一条边得到。只需我们求出没对结点u和v在最小生成树中唯一路径的最大边权dp[i][j],剩下的只需O(m)时间,枚举所有的m-n+1条边进行交换,每次花O(1)时间求出新生成树的权值。总时间复杂度为O(n^2)
方法一:
#include<cstdio> #include<algorithm> using namespace std; const int MAXN=101; int fa[MAXN]; struct point { int x,y; int len; }data[MAXN*MAXN],pre[MAXN*MAXN]; //pre 记录等一下用到了哪些条边 bool operator < (const point &a ,const point &b) //sort 重载比较函数 { return a.len<b.len; } void UFinit(int n) //并查集初始化 { for(int i=1;i<=n;i++) fa[i]=i; } int find(int cur) //带路径压缩的并查集查询函数,简洁而优雅~ { return fa[cur]==cur? cur : fa[cur]=find(fa[cur]); } int kruskal(int n,int m,int &prelen) { UFinit(n); int ans=0; for(int i=0;i<m;i++) { int rootx=find(data[i].x); int rooty=find(data[i].y); if(rootx!=rooty) { fa[rootx]=rooty; ans+=data[i].len; pre[prelen++]=data[i]; } } return ans; } int kruskal2(int n,int m,int nox,int noy)//nox noy 代表直接连接这两个点之间的边不选 { UFinit(n); int ans=0; for(int i=0;i<m;i++) { if(data[i].x==nox && data[i].y==noy) continue; int rootx=find(data[i].x); int rooty=find(data[i].y); if(rootx!=rooty) { fa[rootx]=rooty; ans+=data[i].len; } } return ans; } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&data[i].x,&data[i].y,&data[i].len); int prelen=0; //pre数组的长度 sort(data,data+m); //kruskal 前面的工作 int ans=kruskal(n,m,prelen); //正常版本的kruskal bool unique=true; for(int i=0;i<prelen;i++) { int res=kruskal2(n,m,pre[i].x,pre[i].y);//带去除边的kruskal int cnt=0; for(int j=1;j<=n;j++) if(fa[i]==i) cnt++; if(cnt!=0) //这个很重要!没有就WA,因为可能剩下的不连通,但是恰好res==ans continue; if(res==ans) //如果还有一棵生成树和第一次所求的权值一样,说明最小生成树不唯一 { unique=false; break; } } if(unique) printf("%d\n",ans); else printf("Not Unique!\n"); } return 0; }
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=100+10; int n,m,head[MAXN],len,fa[MAXN],nodes[MAXN],n_len; int dp[MAXN][MAXN]; bool vis[MAXN]; int find(int cur) //带路径压缩的并查集查询函数,简洁而优雅~ { return fa[cur]==cur? cur : fa[cur]=find(fa[cur]); } struct edge //MST邻接表 { int to,next; double val; }e[MAXN*MAXN]; void add(int from,int to,double val) { e[len].to=to; e[len].val=val; e[len].next=head[from]; head[from]=len++; } struct point { int x,y; int len; bool operator < (const point &b) const//sort 重载比较函数 { return len<b.len; } }data[MAXN*MAXN]; void dfs(int cur,int fa,int dis) { for(int i=0;i<n_len;i++) { int x=nodes[i]; dp[cur][x]=dp[x][cur]=max(dp[x][fa],dis); } nodes[n_len++]=cur; for(int i=head[cur];i!=-1;i=e[i].next) { int id=e[i].to; if(id != fa) dfs(id,cur,e[i].val); } } int main() { int T; scanf("%d",&T); while(T--) { memset(head,-1,sizeof(head)); len=0; memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d%d%d",&data[i].x,&data[i].y,&data[i].len); for(int i=1;i<=n;i++) fa[i]=i; //kruskal sort(data,data+m); int mstlen=0; for(int i=0;i<m;i++) { int x=data[i].x,y=data[i].y; int root_x=find(x),root_y=find(y); if(root_x!=root_y) { mstlen+=data[i].len; fa[root_x]=root_y; add(x,y,data[i].len); //kruskal中顺便建立MST的邻接表 add(y,x,data[i].len); vis[i]=true; } } n_len=0; memset(dp,0,sizeof(dp)); dfs(1,-1,0); int mstlen2=9999999; for(int i=0;i<m;i++) { if(vis[i]) continue; int x=data[i].x,y=data[i].y,dis=data[i].len; mstlen2=min(mstlen2,mstlen - dp[x][y] + dis); } // printf("%d\n",mstlen2); if(mstlen == mstlen2) puts("Not Unique!"); else printf("%d\n",mstlen); } return 0; }