畅通工程2(prim模拟链表结构体建图+kru)

畅通工程2(Prim邻接写法+kru)

prim其实很简单的(事实上改了一晚上) 丢人 )

Prim思想
类似dij,每次选取离生成树(已知)最近的点,遍历该点的所有出边,更新dis.
prim中的dis数组含义区别于dij求最短路的算法.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF=999999999;
int T,n,m,tot,cnt,num=1,u,v,w;	char c; int ans,sum; bool flag=0;

struct E{
	int fr,to,val,next;
}e[105*105];

int head[105],dis[105];  bool vis[105];


int main(){
	while(~scanf("%d %d",&m,&n)){
	if(m==0) return 0;			//结束
	sum=0;cnt=0;num=1;flag=0;
	for(int i=1;i<=n;i++){
		dis[i]=INF;
		vis[i]=0;
	}memset(head,-1,sizeof(head)); //初始化
   
	for(int i=1;i<=m;i++){//读入每条边
		scanf("%d%d%d",&u,&v,&w);//默认构造函数
		e[num]={u,v,w,head[u]};
		head[u]=num++;		//u的最后一个出边的序号num;
		e[num]={v,u,w,head[v]};		//加入反边
		head[v]=num++;
	}
	dis[1]=0; vis[1]=1;//从 1->n开始构造生成树
	for(int i=head[1];i!=-1;i=e[i].next){//遍历 1 的所有出边
		if(dis[e[i].to]>e[i].val)  // 更新已生成树到其他未进入生成树点的距离
		dis[e[i].to]=e[i].val;
	}
	int mp=-1;
	for(int i=1;i<=n;i++){
		int minn=INF;
		for(int j=2;j<=n;j++){// 1 已被标记 所以从 2 开始
			if(vis[j]==0&&dis[j]<minn){ // 如果未入树
				minn=dis[j];//
				mp=j;//最近的点的标号
			}//找到离已生成树最近的点
		}
		if(mp==-1){
			flag=1;
   			break;
		}//未找到
		
		vis[mp]=1;//标记该点入树
		for(int k=head[mp];k!=-1;k=e[k].next){//遍历mp的所有出边,更新dis
			if(vis[e[k].to]==0&&dis[e[k].to]>e[k].val){
				dis[e[k].to]=e[k].val;
			}
		}
	}
	for(int i=1;i<=n;i++){//dis数组全部更新完
		if(dis[i]!=INF)
			sum+=dis[i];
		else{
			flag=1;
			break;
		}
	}
	if(flag)   	puts("?");
	else 	printf("%d\n",sum);
	}
		return 0;
}

正经起来了

代码还是有一些问题的(不够完美).我是追求完美的人(挨打).
比如:最后求最小生成树的权值时,是不需要判断. 理由是 在进行n轮的更新dis数组(即MST 拽下英文 )过程中有跳出循环的语句了(见注释未找到).

前天写了kru的代码(吹爆kru) 痴呆型选手的福音(流口水)

kru思想
按权值升序,遍历每一条边,若以联通,则不需要,否则是该边的两点联通.
联通操作使用并查集(吹爆并查集).
总的来说,就是,并查集判联通+贪心选边.不会证明(痴呆型选手不需要会证明)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=(int)(1e9)+7;
const int INF=99999999;
int T,n,m,tot,cnt,num=1; char c;ll sum;bool flag;
int pre[125];
struct edge{
    int fr,to,val;
}e[10050];
bool cmp(edge s1,edge s2){
    return s1.val<s2.val;
}
 
int find(int x){
    int up=x;
    while(up!=pre[up]){
        up=pre[up];
    }
    int k=x,j;
    while(k!=up){
        j=pre[k];
        pre[k]=up;
        k=j;
    }
    return up;
}
 
bool merge(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy){
        pre[fx]=fy;
    return 1;
    }
    return 0;
}
 
int main(){
    while(~scanf("%d %d",&m,&n)){
    if(m==0) return 0;
 
    for(int i=1;i<=n;i++)    pre[i]=i;
    sum=cnt=0;
 
    for(int i=1;i<=m;i++)    scanf("%d %d %d",&e[i].fr,&e[i].to,&e[i].val);
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++){
        if(merge(e[i].fr,e[i].to)){
            sum+=e[i].val;
        }
            if(cnt==n-1){
                break;
        }
    }
    for(int i=1;i<=n;i++){
         if(pre[i]==i){
            cnt++;
         }
    }
//  for(int i=1;i<=n;i++) printf("%d ",pre[i]);
//  cout<<endl;
//  cout<<cnt<<endl;
    if(cnt>=2) puts("?");
    else
        printf("%lld\n",sum);
    }
    return 0;
}

第一篇正经题解,纪念一下.以示我会写裸的MST了.~~
纪念完了

																Dmymax_ly
															2019/3/13   00:07
															目标是徐州邀请赛,永远记得.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值