板子板子板子板子。。。。。。。。。。。。。
1.
k
r
u
s
k
a
l
kruskal
kruskal算法
//Author:melody
#include<bits/stdc++.h>
using namespace std;
const int mm=200010;
const int nn=5010;
int n,m;
int fa[nn];
int ans=0;
struct edge
{
int x,y,z;
}e[mm*2];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
bool mycmp(edge x,edge y)
{
return x.z<y.z;
}
int gf(int x)
{
if(fa[x]==x) return x;
return fa[x]=gf(fa[x]);
}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
e[i].x=read();
e[i].y=read();
int xx=gf(e[i].x);
int yy=gf(e[i].y);
if(xx!=yy) fa[xx]=yy;
e[i].z=read();
}
int cnt=0;
for(int i=1;i<=n;++i)
if(gf(i)==i) cnt++;
if(cnt>=2)
{
cout<<"orz"<<endl;
return 0;
}
//并查集初始化
for(int i=1;i<=n;++i) fa[i]=i;
//按照边权进行排序
sort(e+1,e+1+m,mycmp);/*注意在这里排序的是边数m,不要打错成n*/
//求最小生成树
for(int i=1;i<=m;++i)
{
int xx=gf(e[i].x);
int yy=gf(e[i].y);
if(xx==yy) continue;
fa[xx]=yy;
ans+=e[i].z;
}
cout<<ans<<endl;
return 0;
}
2. P r i m Prim Prim算法
//melody
//Prim
void prim()
{
memset(d,0x3f3f3f,sizeof(d));/*记录被加入集合T时的边权*/
memset(v,0,sizeof(v));/*标记该点是否已经加入了集合T*/
d[1]=0;/*以代价为0的状态加入到集合T*/
for(int i=1;i<=n;++i)
{
int x=0;
for(int j=1;j<=n;++j)
if(!v[j]&&(x==0||d[j]<d[x])) x=j;/*找到集合s中边权最小的点,这一点就是和dijkstra的相似之处,找到全局的最优解*/
v[x]=1;
for(int y=1;y<=n;++y)
if(!v[y]) d[y]=min(d[y],a[x][y]);/*用全局最优解更新其他的点*/
}
}
int main()
{
cin>>n>>m;
//构建邻接矩阵
memset(a,0x3f,sizeof(a));
for(int i=1;i<=n;++i) a[i][i]=0;
for(int i=1;i<=m;++i)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[y][x]=a[x][y]=min(a[x][y],z);
}
//求最小生成树
prim();
for(int i=2;i<=n;++i) ans+=d[i];/*prim的答案是从2开始统计的*/
cout<<ans<<endl;
return 0;
}
我觉得 k r u s k a l kruskal kruskal算法和 P r i m Prim Prim算法不同的是建立基础的思想不一样。
- K r u s k a l Kruskal Kruskal算法是基于边的思想,不断找到边权最小的边,判断是不是可以连接两个不同的连通块,努力去构建你心目中的最小生成树;
- P r i m Prim Prim更侧重的就是点了,目标状态是所有的点都在最小生成树中,建立两个集合T,S;T表示的是已经建成的最小生成树中的点,S表示的是未用过的点,那么不断找到S中的全局最优解加入到集合T中,之后用这个点去更新其他的未被用过的点;重复以上的操作,直到S为空。更像是求最短路中的 D i j k s t r a Dijkstra Dijkstra算法
对比与 D i j k s t r a Dijkstra Dijkstra的相同与不同
//Dijkstra
#include<bits/stdc++.h>
using namespace std;
const int nn=1e6;
const int mm=1e5;
int n,m;
int d[nn],vis[nn];
struct edge
{
int to,ver,nex;
}e[mm*2];
int num=0;
int last[nn];
void add(int x,int y,int z)
{
e[++num].to=y; e[num].ver=z;
e[num].nex=last[x]; last[x]=num;
}
priority_queue<pair<int,int> >q;
void dijkstra(int xx)/*起点*/
{
memset(d,0x3f3f3f,sizeof(d));/*相似点1:都需要两个数组的初始化*/
memset(vis,0,sizeof(vis));
d[xx]=0;/*相似点2:都是根节点以0的状态进入到最优集合T*/
q.push(make_pair(0,xx));
while(q.size())
{
int x=q.top().second; q.pop();
if(vis[x]) continue;/*避免最优集合中的点的重复遍历*/
vis[x]=true;/*相似点3:被选入最优集合的都会有标记*/
for(int i=last[x];i;i=e[i].nex)
{
int y=e[i].to;
int z=e[i].ver;
if(d[x]+z<d[y])/*就算被标记过,也可能再次被更新*/
{/*相似点4:都是用最优集合点去更新其他的点*/
d[y]=d[x]+z;
q.push(make_pair(-d[y],y));
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;++i)//建立邻接表
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
dijkstra(1);//求最短路
for(int i=1;i<=n;++i)
printf("%d\n",d[i]);
return 0;
}
相似点:
- 都需要两个数组的初始化;
- 都是根节点以0的状态进入到最优集合T;
- 被选入最优集合的都会有标记;
- 都是用最优集合点去更新其他的点。
不同的地方:
dijkstra更侧重于邻接表的存储,而prim更侧重于邻接矩阵的存储;
愿你有实力,有才华,主动出击,为自己争取不一样的人生。