最小生成树专题
来源:互联网 发布:angularjs.min.js下载 编辑:程序博客网 时间:2024/06/07 01:34
专题地址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66965#overview
A 裸的最小生成树,Kruskal算法。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=3000;
- const int maxm=100000;
- int parent[maxn];
- int tot;
- struct Edge
- {
- int u,v,w;
- }edge[maxm];
- void addedge(int u,int v,int w)
- {
- edge[tot].u=u;
- edge[tot].v=v;
- edge[tot++].w=w;
- }
- int find(int x)
- {/*
- if(parent[x]==x)
- return x;
- parent[x]=find(parent[x]);
- return parent[x];*/
- return parent[x]==x?x:find(parent[x]);
- }
- bool cmp(Edge a,Edge b)
- {
- return a.w<b.w;
- }
- void init(int n)
- {
- for(int i=0;i<=n;i++)
- parent[i]=i;
- tot=0;
- }
- int kruscal(int n)
- {
- sort(edge,edge+tot,cmp);
- int cnt=0;
- int ans=0;
- for(int i=0;i<tot;i++)
- {
- int u=edge[i].u;
- int v=edge[i].v;
- int w=edge[i].w;
- int t1=find(u);
- int t2=find(v);
- if(t1!=t2)
- {
- parent[t1]=t2;
- ans+=w;
- cnt++;
- }
- if(cnt==n-1)
- break;
- }
- if(cnt<n-1)
- return -1;
- else
- return ans;
- }
- int n;
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- char ch;int m;
- init(n);
- for(int i=1;i<=n-1;i++)
- {
- cin>>ch;
- int u=ch-'A'+1;
- rd(m);
- char to;int w;
- while(m--)
- {
- cin>>to;
- int v=to-'A'+1;
- rd(w);
- addedge(u,v,w);
- }
- }
- printf("%d\n",kruscal(n));
- }
- return 0;
- }
B 裸的最小生成树,用Prim算法。注意两点之间可能有多条边。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=60;
- const int inf=0x3f3f3f3f;
- int cost[maxn][maxn];
- bool vis[maxn];
- int lowc[maxn];
- int n,m;
- int Prim(int cost[][maxn],int n)
- {
- int ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=true;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)
- {
- int minc=inf;
- int p=-1;
- for(int j=0;j<n;j++)
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- if(minc==inf) return -1;//不连通
- ans+=minc;
- vis[p]=true;
- for(int j=0;j<n;j++)
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- return ans;
- }
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- rd(m);
- int u,v,w;
- memset(cost,inf,sizeof(cost));
- while(m--)
- {
- rd3(u,v,w);
- u--;v--;
- if(w<cost[u][v])
- {
- cost[u][v]=w;
- cost[v][u]=w;
- }
- }
- printf("%d\n",Prim(cost,n));
- }
- return 0;
- }
C 空间中有n个小球,给出每个小球的三维坐标和半径,小球可以相互接触也可以重叠,n个小球连接起来,求最小的通道长度。计算任意两个小球之间的距离(注意接触或者重叠距离为0),然后用最小生成树就可以了。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- double cost[maxn][maxn];
- bool vis[maxn];
- const double inf=1000000;
- const double eps=1e-8;
- double lowc[maxn];
- struct Point
- {
- double x,y,z,r;
- void input()
- {
- scanf("%lf%lf%lf%lf",&x,&y,&z,&r);
- }
- double distance(Point p)
- {
- double dis=sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)+(z-p.z)*(z-p.z));
- if(dis-r-p.r>eps)
- return dis-r-p.r;
- else
- return 0;
- }
- }point[maxn];
- int n;
- double Prim(double cost[][maxn],int n)
- {
- memset(vis,0,sizeof(vis));
- double ans=0;
- vis[0]=1;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)
- {
- double minc=inf;
- int p=-1;
- for(int j=0;j<n;j++)
- {
- if(minc>lowc[j]&&!vis[j])
- {
- p=j;
- minc=lowc[j];
- }
- }
- ans+=minc;
- vis[p]=1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- return ans;
- }
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- for(int i=0;i<n;i++)
- {
- point[i].input();
- }
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- cost[i][j]=inf;
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- {
- double dis=point[i].distance(point[j]);
- if(dis<cost[i][j])
- cost[i][j]=cost[j][i]=dis;
- }
- printf("%.3f\n",Prim(cost,n));
- }
- return 0;
- }
D 有N个村庄,给定任意两个村庄之间的距离,并且给出一些已经存在的路(边),问最少共再需要多长的路使得N个村庄联通。最小生成树,本题就是有个条件已经存在一些边了,用Prim算法求的是总长度,且每次都是找的最小的边,那么只要把已经存在的边权值设为0就好了,因为每次都是找最小的边,那么这些边肯定会被选择的,而且用prim算法求完后也正好是题目所求的长度。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=104;
- const int inf=0x3f3f3f3f;
- int cost[maxn][maxn];
- int lowc[maxn];
- bool vis[maxn];
- int n,m;
- int Prim(int cost[][maxn],int n)
- {
- int ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=true;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)//寻找这么多次
- {
- int p=-1,minc=inf;
- for(int j=0;j<n;j++)
- {
- if(minc>lowc[j]&&!vis[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- vis[p]=1;
- ans+=minc;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- return ans;
- }
- int main()
- {
- rd(n);
- memset(cost,inf,sizeof(cost));
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- {
- rd(cost[i][j]);
- }
- rd(m);
- int u,v;
- while(m--)
- {
- rd2(u,v);
- u--;v--;
- cost[u][v]=cost[v][u]=0;
- }
- printf("%d\n",Prim(cost,n));
- return 0;
- }
E 有n个节点,每个节点都有一个权值,然后给出任意两个节点之间的距离,要在两个节点之间连边,使得这n个节点联通,连边的代价除了两点之间边的距离之外还有两点各自的权值之和。在cost矩阵中,加入两个点的权值,然后求最小生成树。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1004;
- const int inf=0x3f3f3f3f;
- int cost[maxn][maxn];
- int lowc[maxn];
- bool vis[maxn];
- int val[maxn];
- int n,m;
- int Prim(int cost[][maxn],int n)
- {
- int ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=true;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)//寻找这么多次
- {
- int p=-1,minc=inf;
- for(int j=0;j<n;j++)
- {
- if(minc>lowc[j]&&!vis[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- vis[p]=1;
- ans+=minc;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- return ans;
- }
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd(n);
- memset(cost,inf,sizeof(cost));
- for(int i=0;i<n;i++)
- rd(val[i]);
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- {
- rd(cost[i][j]);
- cost[i][j]+=val[i]+val[j];
- }
- printf("%d\n",Prim(cost,n));
- }
- return 0;
- }
F 给出N个长度为7的字符串,两个字符串之间定义距离为 两个字符串每个位置字母不同的个数,一个字符串可以生成另一个字符串,代价为两个字符串的距离,问这n个字符串最小的代价。 把任意两个字符串之间的距离求出来,然后进行最小生成树。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=2002;
- const int inf=0x3f3f3f3f;
- int cost[maxn][maxn];
- int lowc[maxn];
- bool vis[maxn];
- string str[maxn];
- int n;
- int cal(int i,int j)
- {
- int cnt=0;
- for(int k=0;k<7;k++)
- if(str[i][k]!=str[j][k])
- cnt++;
- return cnt;
- }
- int Prim(int cost[][maxn],int n)
- {
- int ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=1;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)
- {
- int minc=inf,p=-1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- vis[p]=1;
- ans+=minc;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- return ans;
- }
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- for(int i=0;i<n;i++)
- cin>>str[i];
- memset(cost,inf,sizeof(cost));
- for(int i=0;i<n;i++)
- for(int j=i+1;j<n;j++)
- {
- int w=cal(i,j);
- if(w<cost[i][j])
- cost[i][j]=cost[j][i]=w;
- }
- printf("The highest possible quality is 1/%d.\n",Prim(cost,n));
- }
- return 0;
- }
G 题意为有N个部落以及它们的坐标,然后有P个卫星,如果两个部落都有一个卫星的话,那么这两个部落无论距离多远都能通信,否则,两个部落只能依靠无线电通信,条件是只有两个部落的距离不超过D,两个部落才能通信,求最小的D,使得这N个部落都能通信。也就是在这N个部落里面找N-1条边,使得他们相互连通,P个卫星肯定是放在距离最大的村庄上,这样省去了P-1条边,所以我们只要再找N-1-(P-1)条边就好了,要求最小的D,也就是求这N-1-(P-1)条边里面的最大边的值。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=510;
- const double inf=100000000.0;
- double cost[maxn][maxn];
- double lowc[maxn];
- bool vis[maxn];
- double edge[maxn];
- struct Point
- {
- double x,y;
- void input()
- {
- scanf("%lf%lf",&x,&y);
- }
- double distance(Point p)
- {
- return hypot(x-p.x,y-p.y);
- }
- }point[maxn];
- int n,P;
- double Prim(double cost[][maxn],int n)
- {
- memset(vis,0,sizeof(vis));
- vis[0]=1;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- int cnt=0;
- for(int i=1;i<n;i++)
- {
- double minc=inf;
- int p=-1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- edge[cnt++]=minc;
- vis[p]=1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- sort(edge,edge+cnt);//别忘了排序
- return edge[cnt-P];
- }
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd2(P,n);
- for(int i=0;i<n;i++)
- point[i].input();
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- cost[i][j]=inf;
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++)
- {
- double dis=point[i].distance(point[j]);
- if(dis<cost[i][j])
- cost[i][j]=cost[j][i]=dis;
- }
- printf("%.2f\n",Prim(cost,n));
- }
- return 0;
- }
H 给定n个节点以及这n个节点的坐标,另外给出了m条已经现有的边,求要使这n个点连通,还需要建多少边,输出需要建边的两个端点编号。和上面有个题差不多,也是把给出的边的权值设置为0,然后pre[i]表示i节点所在边的另一个节点,最小生成树选择一条边以后,输出pre[p], p 就可以了。为了避免已经存在的边输出,判断一下是否cost[i][j] >0。因为刚才已存在的边已经赋值为0了。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000;
- const int inf=0x3f3f3f3f;
- bool vis[maxn];
- int cost[maxn][maxn];
- int lowc[maxn];
- int pre[maxn];
- int n,m;
- struct Point
- {
- int x,y;
- int distance(Point p)
- {
- return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
- }
- }point[maxn];
- void Prim()
- {
- memset(vis,0,sizeof(vis));
- for(int i=1;i<n;i++)
- {
- lowc[i]=cost[0][i];
- pre[i]=0;
- }
- vis[0]=1;int p=-1;
- for(int i=1;i<n;i++)
- {
- int minc=inf;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- vis[p]=1;
- if(cost[pre[p]][p])
- printf("%d %d\n",pre[p]+1,p+1);
- for(int j=0;j<n;j++)
- {
- if(lowc[j]>cost[p][j]&&!vis[j])
- {
- lowc[j]=cost[p][j];
- pre[j]=p;
- }
- }
- }
- }
- int main()
- {
- rd(n);
- memset(cost,inf,sizeof(cost));
- for(int i=0;i<n;i++)
- rd2(point[i].x,point[i].y);
- for(int i=0;i<n;i++)
- for(int j=i+1;j<n;j++)
- {
- int dis=point[i].distance(point[j]);
- if(cost[i][j]>dis)
- cost[i][j]=cost[j][i]=dis;
- }
- rd(m);
- int u,v;
- while(m--)
- {
- rd2(u,v);
- u--;v--;
- cost[u][v]=cost[v][u]=0;
- }
- Prim();
- return 0;
- }
[cpp] view plaincopy
- const int maxn=5010;
- int parent[110];
- int n;
- struct Node
- {
- int from,to,edge;
- }node[maxn];
- void init(int n)
- {
- for(int i=1;i<=n;i++)
- parent[i]=i;
- }
- int find(int x)
- {
- return parent[x]==x?x:find(parent[x]);
- }
- bool cmp(Node a,Node b)
- {
- if(a.edge<b.edge)
- return true;
- return false;
- }
- int main()
- {
- while(scanf("%d",&n)!=EOF)
- {
- int m=1;
- int len;
- for(int i=1;i<=n;i++)
- for(int j=1;j<=n;j++)
- {
- scanf("%d",&len);
- if(i<j)
- {
- int temp=m;
- node[temp].from=i;
- node[temp].to=j;
- node[temp].edge=len;
- m++;
- }
- }
- init(n);
- m=n*(n-1)/2;
- len=0;
- sort(node+1,node+1+m,cmp);
- for(int i=1;i<=m;i++)
- {
- int x=find(node[i].from);
- int y=find(node[i].to);
- if(x==y)
- continue;
- else
- {
- len+=node[i].edge;
- parent[x]=y;
- }
- }
- printf("%d\n",len);
- }
- return 0;
- }
J 在一个迷宫里面#代表不可走,空白可走,存在字母一个‘S' 和多个’A‘ ,要从S出发找到所有的A,可以从S出发分成多路去找A,问最少的步数和。也就是求最小生成树,节点为S和A,需要知道任意两个节点的最短距离,因为对每个点都用bfs,找出任意两个点之间的最短距离,然后使用最小生成树就可以了。 注意:输入格式,输入迷宫的大小n*m的时候很坑,要使用字符串格式...
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- const int inf=0x3f3f3f3f;
- int cost[maxn][maxn];
- int lowc[maxn];
- bool vis[maxn][maxn];
- int tot;
- char mp[60][60];
- int n,m;
- int dx[4]={-1,1,0,0};
- int dy[4]={0,0,1,-1};
- struct Point
- {
- int i,j;
- }point[maxn];
- void BFS(int from,int x,int y)
- {
- memset(vis,0,sizeof(vis));
- int step[maxn][maxn];
- step[x][y]=0;
- vis[x][y]=1;
- queue<Point>q;
- Point a;
- a.i=x;a.j=y;
- q.push(a);
- while(!q.empty())
- {
- Point first=q.front();
- q.pop();
- for(int i=0;i<4;i++)
- {
- int newx=first.i+dx[i];
- int newy=first.j+dy[i];
- if(mp[newx][newy]!='#'&&!vis[newx][newy]&&newx>=0&&newx<n&&newy>=0&&newy<m)
- {
- a.i=newx;a.j=newy;
- vis[newx][newy]=1;
- q.push(a);
- step[newx][newy]=step[first.i][first.j]+1;
- }
- }
- }
- for(int i=0;i<tot;i++)
- {
- cost[from][i]=step[point[i].i][point[i].j];
- }
- }
- int Prime(int cost[][maxn],int n)
- {
- bool vis[maxn];
- int ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=1;
- for(int i=1;i<tot;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)
- {
- int minc=inf,p=-1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- p=j;
- minc=lowc[j];
- }
- }
- ans+=minc;
- vis[p]=1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- }
- return ans;
- }
- int main()
- {
- int t;rd(t);
- getchar();
- while(t--)
- {
- gets(mp[0]);
- sscanf(mp[0],"%d%d",&m,&n);//这里的格式
- tot=1;
- for(int i=0;i<n;i++)
- {
- gets(mp[i]);
- int len=strlen(mp[i]);
- for(int k=0;k<len;k++)
- {
- if(mp[i][k]=='A')
- point[tot].i=i,point[tot++].j=k;
- else if(mp[i][k]=='S')
- point[0].i=i,point[0].j=k;
- }
- }
- //construct cost[][]
- memset(cost,inf,sizeof(cost));
- for(int i=0;i<tot;i++)
- BFS(i,point[i].i,point[i].j);
- /*
- for(int i=0;i<tot;i++)
- {
- for(int j=0;j<tot;j++)
- cout<<cost[i][j]<<" ";
- cout<<endl;
- }
- */
- //进行prim
- printf("%d\n",Prime(cost,tot));
- }
- return 0;
- }
K 次小生成树,如果最小生成树唯一的话,输出最小权值,否则输出not unique。
http://blog.chinaunix.net/uid-25324849-id-2182922.html
次小生成树
求最小生成树时,用maxval[i][j],表示最小生成树中i到j路径中的最大边权
求完后,直接枚举所有不在最小生成树中的边(比如该边的两端是i,j,
然后替换掉路径i,j中最大边权的边,更新答案
点的编号从0开始。
为什么可以替换、
求完最小生成树后,从i到j有路径,那么路径上的点两两可以相互到达,如果去掉最大边
,那么该路径就切成了两部分,而加入不在最小生成树的一条边(比如该边地两端是i,j)
,那么又把这两部分重新串起来了,重新变得两两可以相互到达。
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- const int inf=0x3f3f3f3f;
- bool vis[maxn];
- int lowc[maxn];
- int pre[maxn];
- int cost[maxn][maxn];
- int maxval[maxn][maxn];
- bool used[maxn][maxn];
- int Prim(int cost[][maxn],int n)
- {
- int ans=0;
- memset(vis,0,sizeof(vis));
- memset(maxval,0,sizeof(maxval));
- memset(used,0,sizeof(used));
- vis[0]=true;
- pre[0]=-1;
- for(int i=1;i<n;i++)
- {
- lowc[i]=cost[0][i];
- pre[i]=0;
- }
- lowc[0]=0;
- for(int i=1;i<n;i++)
- {
- int minc=inf,p=-1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- if(minc==inf)
- return -1;
- ans+=minc;
- vis[p]=true;
- used[p][pre[p]]=used[pre[p]][p]=true;
- for(int j=0;j<n;j++)
- {
- if(vis[j])
- maxval[j][p]=maxval[p][j]=max(maxval[j][pre[p]],lowc[p]);
- //为什么这么写,vis[j]代表那么节点已经访问过了,该条边是不能加的,有没有边都没关系,否则成了环,
- //那么在路径j->p中,j是路径的起点,p就是路径的终点,而maxval[j][pre[p]],就是起点j到p的上一个节点
- //组成的路径的最大边权值,lowc[p],则是带有p的那一边的权值,二者最大值,就是路径j->p的最大权值
- //比如 1->2->3->4-------->1,虚线代表有边或者没有边都行,那么此时p=4,j=1, pre[p]=3,
- //maxval[j][pre[p]]=maxval[1][3],也就是路径1到3上的最大边的权值,lowc[4]则是3到4上权值,二者的最大值
- //则是路径1->4的最大边的权值。
- if(!vis[j]&&lowc[j]>cost[p][j])
- {
- lowc[j]=cost[p][j];
- pre[j]=p;
- }
- }
- }
- return ans;
- }
- int ans;//保存最小生成树的值
- int smst(int cost[][maxn],int n)//次小生成树
- {
- int minc=inf;
- for(int i=0;i<n;i++)
- for(int j=i+1;j<n;j++)
- {
- if(cost[i][j]!=inf&&!used[i][j])//i,j之间有边,且该边没有在最小生成树中
- {
- minc=min(minc,ans+cost[i][j]-maxval[i][j]);//用该边替换最小生成树中i,j路径中的最大边
- }
- }
- if(minc==inf)
- return -1;
- return minc;
- }
- int n,m;
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd2(n,m);
- memset(cost,inf,sizeof(cost));
- int u,v,w;
- while(m--)
- {
- rd3(u,v,w);
- u--;
- v--;
- if(w<cost[u][v])
- cost[u][v]=cost[v][u]=w;
- }
- ans=Prim(cost,n);
- if(ans==smst(cost,n))
- printf("Not Unique!\n");
- else
- printf("%d\n",ans);
- }
- return 0;
- }
L 裸最小生成树
[cpp] view plaincopy
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- const int maxm=5000;
- struct Edge
- {
- int u,v,w;
- }edge[maxm];
- int tot;//边的个数
- int parent[maxn];
- void addege(int u,int v,int w)
- {
- edge[tot].u=u;
- edge[tot].v=v;
- edge[tot++].w=w;
- }
- void init(int n)
- {
- tot=0;
- for(int i=0;i<=n;i++)
- parent[i]=i;
- }
- int find(int x)
- {
- if(parent[x]==x)
- return x;
- parent[x]=find(parent[x]);
- return parent[x];
- }
- bool cmp(Edge a,Edge b)
- {
- return a.w<b.w;
- }
- int n;
- int kruskal(int n)
- {
- sort(edge,edge+tot,cmp);
- int cnt=0;//计算加入的边数
- int ans=0;
- for(int i=0;i<tot;i++)
- {
- int u=edge[i].u;
- int v=edge[i].v;
- int w=edge[i].w;
- int t1=find(u);
- int t2=find(v);
- if(t1!=t2)
- {
- ans+=w;
- parent[t1]=t2;
- cnt++;
- }
- if(cnt==n-1)
- break;
- }
- if(cnt<n-1)
- return -1;//不连通
- else
- return ans;
- }
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- init(n);
- int u,v,w;
- for(int i=1;i<=n*(n-1)/2;i++)
- {
- rd3(u,v,w);
- addege(u,v,w);
- }
- printf("%d\n",kruskal(n));
- }
- return 0;
- }
N 同裸,浮点数。
[cpp] view plaincopy
- #include <iostream>
- #include <stdio.h>
- #include <algorithm>
- #include <string.h>
- #include <stdlib.h>
- #include <cmath>
- #include <iomanip>
- #include <vector>
- #include <set>
- #include <map>
- #include <stack>
- #include <queue>
- #include <cctype>
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- struct Point
- {
- double x,y;
- double distance(Point p)
- {
- return hypot(x-p.x,y-p.y);
- }
- }point[maxn];
- const double inf=100000.0;
- bool vis[maxn];
- double lowc[maxn];
- double cost[maxn][maxn];
- double Prim(double cost[][maxn],int n)
- {
- double ans=0;
- memset(vis,0,sizeof(vis));
- vis[0]=true;
- for(int i=1;i<n;i++)
- lowc[i]=cost[0][i];
- for(int i=1;i<n;i++)
- {
- double minc=inf;
- int p=-1;
- for(int j=0;j<n;j++)
- {
- if(!vis[j]&&minc>lowc[j])
- {
- minc=lowc[j];
- p=j;
- }
- }
- if(minc==inf)
- return -1.0;
- ans+=minc;
- vis[p]=true;
- for(int j=0;j<n;j++)
- if(!vis[j]&&lowc[j]>cost[p][j])
- lowc[j]=cost[p][j];
- }
- return ans;
- }
- int n;
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd(n);
- for(int i=0;i<n;i++)
- scanf("%lf%lf",&point[i].x,&point[i].y);
- for(int i=0;i<n;i++)
- {
- for(int j=0;j<n;j++)
- cost[i][j]=inf;
- }
- /*
- for(int i=0;i<n;i++)
- {
- for(int j=0;j<n;j++)
- cout<<cost[i][j]<<" ";
- cout<<endl;
- }
- */
- for(int i=0;i<n;i++)
- {
- for(int j=i+1;j<n;j++)
- {
- double dis=point[i].distance(point[j]);
- if(dis<cost[i][j]&&dis>=10&&dis<=1000)
- {
- cost[i][j]=dis;
- cost[j][i]=dis;
- }
- }
- }
- double ans=Prim(cost,n);
- if(ans==-1.0)
- printf("oh!\n");
- else
- printf("%.1f\n",ans*100);
- }
- return 0;
0 0
- 最小生成树专题
- 最小生成树专题
- 最小生成树专题
- 最小生成树专题
- (专题)最小生成树
- 专题六-最小生成树
- UVa10034/POJ2560_Freckles(最小生成树)(小白书图论专题)
- Kuangbin Flying 6最小生成树专题
- 【各大OJ】最小生成树专题
- 【最小生成树入门专题1】G
- 【最小生成树入门专题1】A
- 【最小生成树入门专题1】C
- 【最小生成树入门专题1】H
- UVa10369/POJ2349_Arctic Network(最小生成树)(小白书图论专题)
- UVa10397_Connect the Campus(最小生成树)(小白书图论专题)
- 【 题集 】 【kuangbin带你飞】专题六 最小生成树
- 图论专题小结:最小生成树算法
- [kuangbin带你飞]专题六 最小生成树
- leetcode 120 —— Triangle
- hdoj 1513 Palindrome 【LCS 滚动数组实现】
- leetcode 96: Unique Binary Search Trees
- 高性能网络编程1----accept建立连接
- 序列化必须所有子类都要实现序列化
- 最小生成树专题
- 2.【SELinux学习笔记】概念
- ZOJ 3211 Dream City
- php读写XML文件
- 如何让Qt应用程序只有一个实例
- 通过经纬度坐标计算两个点之间的距离
- mysql数据库基本语法与操作
- 今日你总结
- JS判断字符串是否包含中文