最后一天集训,讲了图论。是一个女老师讲的。最后一天上的是图论。据老师说的,她要是有良心会到最后一天讲图论吗?所以,图论好难啊啊啊啊。
图分为有向图和无向图。
图的存储方式有两种
1) 邻接矩阵
2) 邻接链表
邻接矩阵在数据较小时可以使用,但是如果数据大时,很可能会浪费大量空间,所以一般还是使用邻接链表。
图的最小生成树:
求图的最小生成树大体有两种。
一种是Prim算法:
具体代码实现如下:
int edge[1005][1005];//用邻接矩阵表示的图
int book[1005];//已确定的节点集合
int dis[1005];//最短路径
int num=0;//节点的总个数
int prim(int s) {
int pos, min;//pos记录每次确定下来要加入集合book的那个节点,min表示当前这轮确定的最短路径
int MST = 0;//累加最短路径,表示最短路径和
book[s] = 1;//首先,将节点s放入集合book
pos = s;
for (int i = 1; i <= num; i++) //初始化dis
dis[i] = edge[pos][i];
//执行n-1次
for (int i = 2; i <= num; i++) {
min = INT_MAX;
for (int j = 1; j <= num; j++) {
if (book[j]==0 && min > dis[j]) {
min = dis[j];
pos = j;
}
}
book[pos] = 1;//确定选择出离当前节点最近的那个节点
MST += min;//加上这条最短路径
for (int j = 1; j <= num; j++) //尝试更新dis
if (book[j]==0 && dis[j] > edge[pos][j])//更新条件:j点未访问,加入新点后已访问集合到j的距离变小了
dis[j] = edge[pos][j];
}
return MST;
}
int main() {
int n;
int i,j;
while(cin>>n) {
for(int i=1;i<=n;i++) {
book[i]=0;
dis[i]=0;
for(int j=1;j<=n;j++) {
edge[i][j]=0;
}
}
num=n;
for(i=1;i<=n;i++)//输入邻接矩阵(图)
for(j=1;j<=n;j++)
cin>>edge[i][j];
int ans = prim(1);
cout << ans << endl;
}
return 0
}
它的时间复杂度为O(n^2)
有一种优化的算法,就是Prim+堆。时间复杂度可以优化为O(n log n)
具体代码如下:
int dist(MAXN)
struct node
{
int u,w;
node(){}
node(int u,int w):u(u),w(w){}
friend bool operator<(node a,node b)
{
return a.w>b.w;
}
}
priority_queue<node> q;
void prim(int s)
{
q.push(node);
while (!q.empty())
{
node u=q.top;
q.pop();
flag[u.u]=1;
for (int i=0;i<e[u.u].size();i++)
{
int v=e[u.u][i].v;
int w=w[u.u][i].w;
if (dist[v]>e[i].w)
{
dist[v]=w;
if (!flag[v])
{
q.push(node(v,dist[v]));
}
}
}
}
}
算法证明:
• 图p是一个连通图,Y是对p使用prim算法得到的一棵生成树,Y1是p的一棵最小生成树
• 1.若Y=Y1,显然prim算法是正确的
• 2.若Y≠Y1,可进行如下推导:
• a)Y中有n(n≥1)条边不存在于Y1中,在构建Y的过程中