Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 17785 | Accepted: 6176 |
Description
Input
Output
Sample Input
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
Sample Output
3 Not Unique!
题意:有n个点,其中m条边可以连通。求最小生成树是否唯一
这道题一开始没什么特别好的想法,就是想着把最小生成树当中的每一条边都去掉,然后看看生成树的最小路径是否还相同
这道题要虽然是枚举,不过要注意几个方面:
1. 在枚举的过程中,去掉边之后可能就无法得到完整的树。
2. 我在用Prime做的时候,一开始没有考虑清楚一条边的两个端点。wa了好多次。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #define INF 1000000 //相当于求次小生成树 从最小生成树当中每次删去一边,然后判断生成树是否和最小值相同 //要注意删边之后可能会连通不了 using namespace std; int n,m,p,ans; int map[105][105]; int way[105][2];//用prime算法做的时候,要记录每条边比较麻烦。 //每次加入集合的是点,并不代表最后两个加入的两点之间的那条边就是所取边 //因为这个wa了好多次。。 int prime() { int low[105][2]= {0},visit[105]= {0}; int i,j,ans=0,k; p=0; for (i=0; i<=n; i++) { low[i][0]=map[1][i]; low[i][1]=1; } visit[1]=1; way[p][0]=1; for (i=1; i<n; i++) { int max=INF; k=-1; for (j=1; j<=n; j++) if (!visit[j] && low[j][0]<max) { max=low[j][0]; k=j; } if (k==-1) return 0; way[p][0]=low[k][1]; way[p++][1]=k; visit[k]=1; ans+=low[k][0]; for (j=1; j<=n; j++) if(!visit[j] && low[j][0]>map[k][j]) { low[j][0]=map[k][j]; low[j][1]=k; } } return ans; } int find() { int low[105]= {0},visit[105]= {0}; int i,j,s=0,k; for (i=0; i<=n; i++) low[i]=map[1][i]; visit[1]=1; for (i=1; i<n; i++) { int max=INF; k=-1; for (j=1; j<=n; j++) if (!visit[j] && low[j]<max) { max=low[j]; k=j; } if (k==-1) return -1; visit[k]=1; s+=low[k]; for (j=1; j<=n; j++) if(!visit[j] && low[j]>map[k][j]) low[j]=map[k][j]; } return s; } int main () { int t,i,j; cin>>t; while(t--) { cin>>n>>m; for (i=0; i<=n; i++) { way[i][0]=way[i][1]=0; for (j=0; j<=n; j++) map[i][j]=INF; } for (i=0; i<m; i++) { int a,b,c; cin>>a>>b>>c; map[a][b]=map[b][a]=c; } ans=prime(); int flag=0; int x,y,temp,s; i=0; while(i<p) { x=way[i][0]; y=way[i][1]; temp=map[x][y]; map[x][y]=map[y][x]=INF; s=find(); if (s==ans) { flag=1; break; } map[x][y]=map[y][x]=temp; i++; } if (flag) cout<<"Not Unique!"<<endl; else cout<<ans<<endl; } return 0; }
还有一个N^2的算法。在做一次prim的时候,将树上两点之间的最大边记录下来。然后N^2的枚举不在树上的点,加入之后一定成为一个环,取代最大的边。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <vector> #define INF (1 << 30) using namespace std; const int maxn = 110; const int maxm = 11000; typedef int type; int n, m; int vis[maxn], dis[maxn], pre[maxn]; int g[maxn][maxn], mx[maxn][maxn], mst[maxn][maxn]; vector<int> V[maxn]; struct node { int u; type l; friend bool operator < (node s, node v) { return s.l > v.l; } }; void init() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { V[i].clear(); vis[i] = 0; dis[i] = INF; for (int j = 1; j <= n; j++) { g[i][j] = INF; mx[i][j] = 0; mst[i][j] = 0; } g[i][i] = 0; } int u, v, l; while(m--) { scanf("%d%d%d", &u, &v, &l); g[u][v] = g[v][u] = l; V[u].push_back(v); V[v].push_back(u); } } int prim() { priority_queue<node> q; node tmp, now; now.u = 1, now.l = 0; q.push(now); dis[1] = 0; int ans = 0; while(!q.empty()) { now = q.top(); q.pop(); int u = now.u; if (vis[u]) continue; for (int i = 1; i <= n; i++) if (vis[i]) mx[i][u] = mx[u][i] = max(mx[i][pre[u]], g[u][pre[u]]); mst[pre[u]][u] = mst[u][pre[u]] = 1; vis[u] = 1; ans += now.l; for (int i = 0; i < V[u].size(); i++) { int v = V[u][i]; if (!vis[v] && g[u][v] < dis[v]) { dis[v] = g[u][v]; pre[v] = u; tmp.u = v, tmp.l = dis[v]; q.push(tmp); } } } return ans; } int sec_mst(int x) { int ans = INF; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) if (!mst[i][j] && g[i][j] < INF) { ans = min(ans, x - mx[i][j] + g[i][j]); } } return ans; } int main () { int t; scanf("%d", &t); while(t--) { init(); int ans = prim(); int dd = sec_mst(ans); if (ans == dd) puts("Not Unique!"); else printf("%d\n", ans); } return 0; }