POJ 1258
Agri-Net
http://poj.org/problem?id=1258;
两种算法 Prim Kruskal.
先说Prim
初始化 权值,随便一个顶点做起点,为0 其它的为最大值。
1. 找到权值最小的顶点,且没有加入集合。
2. 把顶点权值加到结果,把定点加入集合。
3. 暴力枚举 顶点连接的所有的边,更新所有能够连接上顶点的权值。
4. 重复前3步,直到所有点全部加入集合。
以上图就是 首先找到的是 0节点 一开始权值是0,所以res +=0;然后暴力所有能够连接的点就是 1,和2, 然后更新 mincost[2]=min(INF,2),结果等于2;同理 mincost[1]=10;
其它点的权值不变。 然后又开始找找到 2 顶点,然后暴力所有点,这次更新的就是3 4 5顶点。然后不断重复就行了
最后连接起来的树是这个样子的。
下面是代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const long long mod=1e9+7;
const int maxv=1e3+25; //最大顶点数
const int INF=0x7fffffff;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
int cost[maxv][maxv]; // cost[u][v] 表示 u 到 v 的权值如果没有边权值为无穷大(INF);
int mincost[maxv]; //每个点的最小权值,起点自己为0,其它为自己连接到集合最小权值;
bool used[maxv]; //表示顶点 是否已经连接上;
int V; //顶点数目;
int prim() {
for(int i=0; i<V; i++) {
mincost[i]=INF; //开始的时候全部初始化为无穷大。
used[i]=false; //初始化 ,全部没有连接。
}
mincost[0]=0; //以 0 节点为起点开始连接。
int res = 0; //权值和
while(1) {
int v=-1; //选择的节点,开始为-1,然后开始找已经确定是最小的距离
for(int u=0; u<V; u++) {
if(!used[u]&& (v == -1 || mincost[u]<mincost[v]))v=u; //如果 v==-1或者不是最小距离的时候更新v。
}
if(v==-1)break; //只有在所有的点都已经加入集合 v==-1。跳出循环。
used[v]=true; //把顶点加入集合。
res+=mincost[v]; //把边的长度加到结果里
for(int u=0; u<V; u++) {
mincost[u]=min(mincost[u],cost[v][u]); //从当前点到其它点的距离如果比其他路短就更新。
}
}
return res;
}
int main() {
int m;
scanf("%d%d",&V,&m); //输入顶点数和边数。 注意定点是从 0 到V-1。
for(int i=0; i<m; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
cost[u][v]=cost[v][u]=w;
}
printf("%d\n",prim());
//下面是POJ 1258 主程序代码。
/*
while(cin>>V) {
for(int i=0; i<V; i++) {
for(int j=0; j<V; j++) {
cin>>cost[i][j];
}
}
printf("%d\n",prim());
}
*/
return 0;
}
接下来是Kruskal 代码
这个算法其实和Prim 算法差距不大,这个是直接把边排序
1. 找到最小的边
2. 判断边的两个节点是不是连接到同一颗树上,是跳过,不是连接两个顶点的根节点,结果加上边。
3. 重复上面两步,直到连接N-1条边,因为N个顶点要N-1条边就能连接起来。
对于这个图,首先就找到(2,3 )这条边连起来,然后就是(4 5),然后(0 2),这时候有2棵树
(0 2 3 )和(4 5)然后接着连 (2 5)(5 6 )(1 4)就全部连接上了
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const long long mod=1e9+7;
const int maxv=1e3+25; //最大顶点数
const int maxm=1e6+25; //最大边数
const int INF=0x7fffffff;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
int V,m; //顶点数目;
struct edge {
int u,v,cost; //节点用来 保存每个边的情况
};
bool cmp(const edge & a,const edge & b) {
return a.cost<b.cost; //用于排序,相当于重载小于号。
}
edge es[maxm];
int par[maxv]; // par[i]==j. i 的 根节点为 j;
int find(int x) { //寻找根节点
if(x==par[x])return x; //如果根节点就是自己直接返回自己
else return par[x]=find(par[x]); //如果根节点不是自己,继续寻找自己上一个节点的根节点。
}
void unit(int x,int y) {
x=find(x); //找到 x 的根节点
y=find(y);
par[x]=y; //把 x 的根节点 连接上 y.
}
void init(int n) {
for(int i=0; i<=n; i++)par[i]=i; //初始化的时候根节点都是自己;
}
int Kruskal() {
sort(es,es+m,cmp); //按 边的权值排序;
init(V);
int res=0,se=0; //se 保存边数。
for(int i= 0; i<m; i++) {
edge e =es[i];
if(find(e.u)!=find(e.v)) { //如果根节点不相同就连接
unit(e.u,e.v);
res+=e.cost;
if(++se==V-1)return res; //如果连了V-1条边就跳出;
}
}
return res;
}
int main() {
scanf("%d%d",&V,&m); //输入顶点数和边数。 注意定点是从 0 到V-1。
for(int i=0; i<m; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
es[i].u=u;
es[i].v=v;
es[i].cost=w;
}
printf("%d\n",Kruskal());
// POJ 1285 AC 主程序代码
/*
while(cin>>V) {
int k=0;
for(int i=0; i<V; i++) {
for(int j=0; j<V; j++) {
cin>>es[k].cost;
es[k].u=i;
es[k].v=j;
k++;
}
}
m=k;
printf("%d\n",Kruskal());
}
*/
return 0;
}