首页 > 试题广场 >

继续畅通工程

[编程题]继续畅通工程
  • 热度指数:10013 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
    省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。

输入描述:
    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。

    当N为0时输入结束。


输出描述:
    每个测试用例的输出占一行,输出全省畅通需要的最低成本。
示例1

输入

3
1 2 1 0
1 3 2 0
2 3 4 0
3
1 2 1 0
1 3 2 0
2 3 4 1
3
1 2 1 0
1 3 2 1
2 3 4 1
0

输出

3
1
0
/*常规的并查集方法,构建最小生成树,用sort对代价大小进行排序。这里只要稍微修改,小于号的重载方式,即优先让建好的排在前面,就能够优先参与并查操作,需要用到未建通路时再加上算总代价*/




#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 100
int tree[N];
int findroot(int x){
if(tree[x]==-1)return x;
else{
int tmp=findroot(tree[x]);
tree[x]=tmp;
return tmp;
}
}
struct edge{
int a,b,status;
int cost;
bool operator <(const edge &a)const{ 
//重载 将状态为1的放在前面 优先并查 其余按代价升序排列
if(status==a.status)
return cost<a.cost;
else return status>a.status;
}
}edge[5000];
int main(){
int n;
while(scanf("%d",&n)!=EOF&&n!=0){
for(int i=1;i<=n*(n-1)/2;++i)
scanf("%d%d%d%d",&edge[i].a,&edge[i].b,&edge[i].cost,&edge[i].status);
sort(edge+1,edge+1+n*(n-1)/2);
for(int i=1;i<=n;++i)tree[i]=-1;
int ans=0;
for(int i=1;i<=n*(n-1)/2;++i){
int a=findroot(edge[i].a);
int b=findroot(edge[i].b);
if(a!=b){
tree[a]=b;
if(edge[i].status==0)ans+=edge[i].cost; //只有状态为0才参与计算
}
}
printf("%d\n",ans);
}
return 0;
}
编辑于 2018-01-22 00:15:53 回复(0)

Kruskall求最小生成树的改版,题目已经提前提供了一些边

发表于 2016-10-28 14:57:54 回复(0)
import java.util.Scanner;
import java.util.Arrays;
import java.util.Comparator;

class edge{
    private int u;
    private int v;
    private int w;
    
    public edge(int u, int v, int w) {
        this.u = u;
        this.v = v;
        this.w = w;
    }
    public int getU() {
        return u;
    }
    public int getV() {
        return v;
    }
    public int getW() {
        return w;
    }
    public void setW(int w){
        this.w = w;
    }
}
public class Main {
    static int f[] = new int[100];
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int n = input.nextInt();    //顶点数
        int m = n*(n-1)/2;    //边数
        edge e[] = new edge[m];
        for(int i=0;i<m;i++){
            int u = input.nextInt();
            int v = input.nextInt();
            int w = input.nextInt();
            e[i] = new edge(u,v,w);
            int status = input.nextInt();
            if(status == 1){
                e[i].setW(0);
            }
        }
        //按边的权值从小到大排序
        Arrays.sort(e, new Comparator<edge>(){
            public int compare(edge o1, edge o2) {
                if(o1.getW()>o2.getW()){
                    return 1;
                }else if(o1.getW()<o2.getW()){
                    return -1;
                }else{
                    return 0;
                }
            }
        });
        //并查集初始化,数组里存的是自己数组的下标编号
        for(int i=0;i<n;i++){
            f[i] = i;
        }
        //Kruskal算法核心部分
        int count = 0,sum = 0;
        for(int i=0;i<m;i++){        //从小到大枚举每一条边
            if(merge(e[i].getU(),e[i].getV())){        //判断两个点是否联通,不连通则用这条边
                count++;
                sum += e[i].getW();
            }
            if(count == n-1){
                break;
            }
        }
        System.out.println(sum);
    }
    //并查集合并两个子集合
    public static boolean merge(int u,int v){
        int t1 = getf(u);
        int t2 = getf(v);
        if(t1 != t2){    //判断两个点是否在同一个集合中
            f[t2] = t1;
            return true;
        }
        return false;
    }
    //并查集寻找祖先
    public static int getf(int v){
        if(f[v]==v){
            return v;
        }else{
            f[v] = getf(f[v]);        //路径压缩
            return f[v];
        }
    }
}

发表于 2018-06-12 18:30:25 回复(0)
/**
这题使用了并查集的思想,造了最小生成树,因为事先按花费排序,所以这棵树一旦生成,就是代价最小
**/
#include<iostream>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
int tree[101];
int findroot(int x) {
    if (tree[x] == -1)
        return x;
    else {
        int tmp = findroot(tree[x]);
        tree[x] = tmp;
        return tmp;
    }
}
struct e{
    int l,r,fee;
     bool operator<(const e &a) const {
       return fee < a.fee;
    }
} e[5000];
int main() {
    int n=0;

    int left,right,fee,status;
    while(cin >>n && n) {

        for (int i = 1; i <= n * (n - 1) / 2; ++i){
             cin>>left>>right>>fee>>status;
        if(status == 1) {
            e[i].l = left;
            e[i].r = right;
            e[i].fee = 0;
        } else {
           e[i].l = left;
           e[i].r = right;
           e[i].fee = fee;
        }
        }
        sort(e + 1, e + 1 + n * (n - 1) / 2);
        for (int i = 1; i <= n; ++i)
            tree[i] = -1;

        int ans = 0;
        for (int i = 1; i <= n * (n - 1) / 2; ++i) {
            int a = findroot(e[i].l);
            int b = findroot(e[i].r);
            if (a != b) {
                tree[a] = b;
                ans += e[i].fee;
            }
        }

        cout<<ans<<endl;
    }


}

发表于 2018-06-03 15:04:20 回复(3)
还是畅通工程的改版~
#include <stdio.h>

typedef struct Edge{
    int from,to,len,flag;
}Edge;

Edge e[10000];
int dad[100],h[100];

void Initial(int n)
{
    for(int i=0;i<=n;i++)
    {
        dad[i]=i;
        h[i]=0;
    }    
}

int Find(int x)
{
    if(x!=dad[x])
        dad[x]=Find(dad[x]);
    return dad[x];
}

void Union(int x,int y)
{
    x = Find(x);
    y = Find(y);
    if(x!=y)
    {
        if(h[x]<h[y])    dad[x]=y;
        else if(h[x]>h[y])    dad[y]=x;
        else{
            dad[y]=x;
            h[x]++;
        }
    }
    return;
}

int cmp(const void *a,const void *b)
{
    if((*(Edge*)a).flag==(*(Edge*)b).flag)
        return (*(Edge*)a).len-(*(Edge*)b).len;
    else
        return (*(Edge*)b).flag-(*(Edge*)a).flag;
}

int Kruskal(int n,int num)
{
    Initial(n);
    qsort(e,num,sizeof(e[0]),cmp);
    int sum = 0;
    for(int i=0;i<num;i++)
    {
        if(Find(e[i].from)!=Find(e[i].to))
        {
            Union(e[i].from, e[i].to);
            if(e[i].flag==0)
                sum+=e[i].len;
        }
    }
    return sum;
}

int main()
{
    int n,m,ans,i;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        m=n*(n-1)/2;
        for(i=0;i<m;i++)
        {
            scanf("%d %d %d %d",&e[i].from,&e[i].to,&e[i].len,&e[i].flag);
        }
        ans=Kruskal(n, m);
        printf("%d\n",ans);
    }
    return 0;
}


发表于 2021-02-28 22:42:45 回复(0)
/*
*
*克鲁斯卡尔最小生成树,重点是边的排序,这里是否建立优先,其次是花费(能不花就不花)。
*但是,是否已经建立的情况下,花费的排序就多余了,可以再增加一个距离(花费优先)。
*/

#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e4;
struct edge
{
    int u, v, f, w;
    edge(int _u=0,int _v=0,int _w=0,int _f=0):u(_u),v(_v),w(_w),f(_f){}
};
vector<edge> edges;      //边集
int father[maxn+5];      //并查集表征连通分量
int n, m;

bool cmp(edge a, edge b)   //当然是能不花钱就不花钱  但是貌似f==1时,w的大小比较没啥用(可以再增加一个距离,花费优先)
{
    if(a.f == b.f) return a.w < b.w;
    else return a.f > b.f;
}

void Initial()   //初始化并查集
{
    for(int i = 1;i <= n; i++) father[i] = i;
}

int getFather(int x)   //找根
{
    int a = x;
    while(x != father[x]) x = father[x];
    //路径压缩
    while(a != father[a])
    {
        int z = a; a = father[a]; father[z] = x;
    }
    return x;
}

void Create()    //建图
{
    int u, v, w, f;
    for(int i = 0;i < m; i++)
    {
        cin >> u >> v >> w >> f;
        edges.push_back(edge(u, v, w, f));
    }
}

int Kruskal()     //克鲁斯卡尔
{
    Initial();
    sort(edges.begin(), edges.end(), cmp);
    int ans = 0, num = 0;
    for(int i = 0;i < edges.size(); i++)
    {
        int fu = getFather(edges[i].u);
        int fv = getFather(edges[i].v);
        if(fu != fv)     //u  和  v  不连通则连通   如果连通相连就会形成环(×) 
        {
            father[fu] = fv;
            ans += edges[i].f ? 0 : edges[i].w; num++;
            if(num == n-1) break;
        }
    }
    //if(num != n-1) return -1;   //图非连通 
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);    
    while(cin >> n && n)
    {
        m = n*(n-1)/2;
        Create();
        int ans = Kruskal();
        //if(ans == -1) cout << "Error" << '\n';   //图非连通
        cout << ans << '\n';
    }
    return 0;
}

发表于 2021-01-22 20:45:40 回复(0)
#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
struct edge
{
	int start, end, cost, built;
	edge(int s, int e, int c, int b) :start(s), end(e), cost(c), built(b) {};
};

int find_root(map<int, int>& V, int x)//查找x所在集合的根
{
	if (V.find(x) != V.end() && V[x] != x)
		V[x] = find_root(V, V[x]);//递归进行路径压缩
	else
		return x;
	return V[x];
}

int kruskal(vector<edge>& E, int n)
{
	int cost = 0, count = 0;
	map<int, int> V;
	vector<edge>::iterator it = E.begin();
	while (count != n - 1)
	{
		while (it != E.end())
		{			
			int a = find_root(V, it->start);
			int b = find_root(V, it->end);
			//注意要保证不在集合内,因为可能已建成的形成了环路
			if (a != b)
			{
				if (it->built == 1)
				{
					V[b] = a;
					it++;
					break;
				}
				else
				{
					V[b] = a;
					cost += it->cost;
					it++;
					break;
				}
			}
			else
				it++;
		}
		count++;
	}
	return cost;
}
bool comp(edge e1, edge e2)//排序函数
{
	if (e1.built == e2.built)
		return e1.cost < e2.cost;
	else
		return e1.built > e2.built;
}
int main()
{
	int n;
	vector<edge> E;
	while (cin>>n && n != 0)
	{
		int edge_num = n * (n - 1) / 2;
		for (int i = 0; i < edge_num; i++)
		{
			int s, e, c, b;
			cin >> s >> e >> c >> b;
			E.push_back(edge(s, e, c, b));
		}
		sort(E.begin(), E.end(), comp);
		cout << kruskal(E, n) << endl;
		E.clear();
	}
	return 0;
}

kruskal算法,注意已经建成的形成了环路
编辑于 2020-07-01 16:03:20 回复(0)
当然Kruskal更方便,但是为了应付考试和增强自己的能力,这道题我用了Prim算法来完成
#include<iostream>
(720)#include<algorithm>
#include<cstring>
(803)#include<limits.h>
using namespace std;
struct Edge{
    int IsOk;
    int cost;
}e[101][101];
int lowcost[101];
bool visit[101];
int main(){
    int n;
    int x,y,money,statu;
    while(cin>>n){
        if(n==0)    break;
        int spend=0;
        memset(visit,false,sizeof(visit));
        int AllE=n*(n-1)/2;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                e[i][j].cost=INT_MAX;
        for(int i=0;i<AllE;i++){
            cin>>x>>y>>money>>statu;
            e[x][y].cost=money;
            e[x][y].IsOk=statu;
            e[y][x].cost=money;
            e[y][x].IsOk=statu;
        }
        for(int i=2;i<=n;i++){
            if(e[1][i].IsOk)    lowcost[i]=0;
            else    lowcost[i]=e[1][i].cost;
        }
        visit[1]=true;
        for(int i=2;i<=n;i++){
            int min=INT_MAX;
            int v=-1;
            for(int j=1;j<=n;j++){
                if(visit[j]==false){
                    if(min>lowcost[j]){
                        min=lowcost[j];
                        v=j;
                    }
                }
            }
            if(v!=-1){
                spend+=min;
                visit[v]=true;
                for(int j=1;j<=n;j++){
                    if(visit[j]==false){
                        if(e[v][j].IsOk)    lowcost[j]=0;
                        else if(lowcost[j]>e[v][j].cost)    lowcost[j]=e[v][j].cost;
                    }
                }
            }
        }
        cout<<spend<<endl;
    }
    return 0;
}


发表于 2020-03-24 19:35:49 回复(4)
#include<iostream>//并查集思想,此题有考虑到MST不存在的情况,要自己构造
#include<cmath>//先将flag=1的节点加入到集合中
#include<algorithm>
#include<iomanip>
using namespace std;
#define N 101
int tree[N];//保存各个节点的根节点
struct e//结构体保存两个节点之间的cost(距离,时间,建路建桥花费之类的)
{
	int a, b;
	int cost;
	bool flag;//此题有标志,1表示已修了路,0表示没修
	bool operator<(const e& s)const//重载<号
	{
		if (flag != s.flag)//flag=1的排在最前面,以便等下遍历的时候,把已经修了路的节点先放入一个集合中
			return flag > s.flag;
		else
		{
			if (cost != s.cost)//flag=0时,按照修路花费最少的排在前面
				return cost < s.cost;
			else//这个随便 你a小排前还是b小排前不影响
				return  a < s.a;
		}
	}
}edg[5500];
int findroot(int x)//找各个节点所在树的根节点
{
	if (tree[x] == -1)return x;//找到根节点,输出根节点
	else//路径压缩,比如1-2-3,3的祖先节点是2,进行路径压缩,递归查找2的祖先节点=1,
	{	//将3的祖先节点设为1,即1-2,1-3,此时1-3这条边的权值=以前2-3的权值,其实只是移动了边;
		int tmp = findroot(tree[x]);
		tree[x] = tmp;
		return tmp;
	}
}
int main()
{
	int n;
	while (cin >> n && n != 0)
	{
		for (int i = 1; i <= n; i++)//一开始的节点都是单独的连通分量
			tree[i] = -1;
		for (int i = 1; i <= n * (n - 1) / 2; i++)//输入数据
			cin >> edg[i].a >> edg[i].b >> edg[i].cost >> edg[i].flag;
		sort(edg + 1, edg + 1 + n * (n - 1) / 2);//排序
		int ans = 0;
		for (int i = 1; i <= n * (n - 1) / 2; i++)
		{
				int a = findroot(edg[i].a);//查找两个点的根节点
				int b = findroot(edg[i].b);
				if (a != b)//a!=b,说明两点不是在同一棵树上,要将其合并成同一个集合
				{
					tree[a] = b;// 将其合并成同一个集合,一棵树变成另一棵树根节点的子树
					if (edg[i].flag != 1)//判断,如果flag=1,则路已修不用再花费,反之,flag=0,要修路ans+=edg[i].cost,因为当flag=0时,我们的
						//edg数组是按cost的升序排列的,所以先修花费小的路,
					{
						ans += edg[i].cost;
					}
				}
		}//当所有的节点都在一棵树的时候,ans保持不变,直至循环退出,ans即为最小花费
		cout << ans << endl;
	}
	return 0;
}

编辑于 2020-03-20 00:32:33 回复(3)
/*思路很简单,先不考虑已经建造好的路,于是题目变成已知各边求最小生成树,很显然用克鲁斯卡尔算法就OK,又再考虑有已经建造好的路,因此这段路不需要再掏钱,所以当两点之间路已经建造好时,把费用设为0就行了*/

# include<stdio.h>
#
include<algorithm>
using namespace std;
const int maxn=110;
int father[maxn];
int result=0;
struct path {
    int a;   //路的起点
    int b;   //终点
    int d;   //花费
   
} p[5000];
 void Init(){    //初始化
     result=0;
     for(int i=0;i<maxn;i++){
         father[i]=i;
     }
 }
int findfather(int x){ //并查集找爹
    while(father[x]!=x)
        x=father[x];
    return x;
}
void Union(int x,int y){  //并查集合并
    int faA=findfather(x);
    int faB=findfather(y);
    father[faA]=faB;
   
}
bool cmp(path x,path y){  //克鲁斯卡尔需要给边排序
    return x.d<y.d;
}

int main(){
    int start,end,dis,flag;
    int i,j;
    int n;
    Init();
    while(scanf("%d",&n)!=EOF){
        if(n==0)
            break;
        int np=n*(n-1)/2;
        for(i=0;i<np;i++){
            scanf("%d%d%d%d",&start,&end,&dis,&flag);
            p[i].a=start;
            p[i].b=end;
            p[i].d=dis;
            if(flag==1)
                p[i].d=0;
        }
        sort(p,p+np,cmp);
        for(j=0;j<np;j++){
            if(findfather(p[j].a)!=findfather(p[j].b)){
                Union(p[j].a,p[j].b);
                result+=p[j].d;
            }
        }
        printf("%d\n",result);
        Init();
       
       
    }
   
    return 0;
}










发表于 2020-03-18 15:26:01 回复(0)
输入时将已修的路权值置为0
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn=10005;
int root[maxn];
struct edge
{
	int a,b,w,flag;
}r[maxn];
bool cmp(edge a,edge b)
{
	return a.w<b.w;
}
int getroot(int x)
{
	if(x!=root[x])
		root[x]=getroot(root[x]);
	return root[x];
}
int kruskal(int m)
{
	int ans=0;
	for(int i=0;i<maxn;++i)		root[i]=i;
	sort(r,r+m,cmp);
	for(int i=0;i<m;++i)
	{
		int ra=getroot(r[i].a), rb=getroot(r[i].b);
		if(ra!=rb)
		{
			root[ra]=rb;
			ans+=r[i].w;
		}
	}
	return ans;
}
int main()
{
	int n;
	while (cin>>n&&n)
	{
		int m=n*(n-1)/2;
		for(int i=0;i<m;++i)
		{
			cin>>r[i].a>>r[i].b>>r[i].w>>r[i].flag;
			if(r[i].flag)	r[i].w=0;			  //已修的路权值视为0
		}
		cout<<kruskal(m)<<endl;
	}
	return 0;
}

发表于 2020-01-12 20:24:05 回复(0)

在输入时就对已建好的路进行树的生成,且不保存,而后只保存未开始修建的路,然后进行排序和树的继续生成。

def findRoot(village):
    global villages
    if villages[village] == -1:
        return village
    else:
        temp = findRoot(villages[village])
        villages[village] = temp
        return temp
while True:
    try:
        n = int(input())
        if n == 0:
            break
        villages = [-1 for i in range(n+1)]
        nodeNum = 1
        roads = []
        for i in range(n*(n-1)//2):
            temp = list(map(int, input().split()))
            if temp[3] == 1:
                a = findRoot(temp[0])
                b = findRoot(temp[1])
                if a != b:
                    villages[a] = b
                    nodeNum += 1
            else:
                roads.append(temp)
        costs = 0
        roads.sort(key=lambda x:x[2])
        for i in range(len(roads)):
            a = findRoot(roads[i][0])
            b = findRoot(roads[i][1])
            if a != b:
                villages[a] = b
                costs += roads[i][2]
                nodeNum += 1
            if nodeNum == n:
                break
        print(costs)
    except Exception:
        break
编辑于 2018-09-28 16:01:24 回复(0)
最小生成树算法,排序函数稍微变一下,把已经建好的排在最前面,其余按cost递增排序,遍历边时遇到没建好的再加上cost。
#include<stdio.h>
#include<algorithm>
#define N 100
using namespace std;
typedef struct Edge{
    int a,b;
    int cost;
    int flag;
}Edge;
int Tree[N];
int FindRoot(int x){
    if(Tree[x]==-1) return x;
    else{
        Tree[x]=FindRoot(Tree[x]);
        return Tree[x];
    }
}
bool cmp(Edge a,Edge b){
    if(a.flag!=b.flag)
        return a.flag>b.flag;
    else
        return a.cost<b.cost;
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF&&n!=0){
        Edge e[N*(N-1)/2];
        int i;
        for(i=1;i<=n;i++)
            Tree[i]=-1;
        for(i=0;i<n*(n-1)/2;i++)
            scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].cost,&e[i].flag);
        sort(e,e+n*(n-1)/2,cmp);
        int ans=0;
        int a,b;
        for(i=0;i<n*(n-1)/2;i++){
            a=FindRoot(e[i].a);
            b=FindRoot(e[i].b);
            if(a!=b){
                Tree[b]=a;
                if(e[i].flag==0)
                    ans+=e[i].cost;
            }
        }
        printf("%d\n",ans); 
    }
    return 0;
}

发表于 2018-01-21 22:14:18 回复(0)
两种思路,一种是如果为1 则代价为0 其余相同
另一种是如果为1 则合并两个点 不加入排序
发表于 2019-05-15 15:47:44 回复(0)
#include<bits/stdc++.h>
using namespace std;

const int Max=100;

struct Edge {
    int f;
    int t;
    int c;
    bool operator < (const Edge & a) {
        return c<a.c;
    }
};

Edge edge[Max*Max];
int father[Max];
int height[Max];
bool status[Max*Max];

void Initial(int n) {
    for(int i=0; i<=n; i++) {
        father[i]=i;
        height[i]=0;
    }
    return ;
}

int Find(int x) {
    if(x!=father[x]) {
        father[x]=Find(father[x]);
    }
    return father[x];
}

void Union(int x,int y) {
    x=Find(x);
    y=Find(y);
    if(x!=y) {
        if(height[x]>height[y]) {
            father[y]=x;
        } else if(height[x]<height[y]) {
            father[x]=y;
        } else {
            father[y]=x;
            height[x]++;
        }
    }
    return ;
}

int Kruskal(int n) {
    sort(edge,edge+n*(n-1)/2);
    int sum=0;
    for(int i=0; i<n*(n-1)/2; i++) {
        if(Find(edge[i].f)!=Find(edge[i].t)) {
            Union(edge[i].f,edge[i].t);
            sum+=edge[i].c;
        }
    }
    return sum;
}

int main() {
    int n;
    while(cin>>n) {
        Initial(n);
        for(int i=0; i<n*(n-1)/2; i++) {
            cin>>edge[i].f>>edge[i].t>>edge[i].c>>status[i];
            if(status[i]) {
                edge[i].c=0;
            }
        }
        cout<<Kruskal(n)<<endl;
    }
    return 0;
}
发表于 2022-10-13 19:27:30 回复(1)
#include <iostream>
#include <algorithm>
using namespace std;

int father[100];
int height[100];

struct Edge{
    int from,to;
    int cost;
    int isBuild;
}edge[5000];

bool cmp(Edge a,Edge b){
    if(a.isBuild!=b.isBuild){
        return a.isBuild>b.isBuild;
    }
    return a.cost<b.cost;
}

void init(int n){
    for(int i=1;i<=n;i++){
        father[i]=i;
        height[i]=0;
    }
}
int find(int x){
    if(father[x]==x){
        return x;
    }else{
        int tmp=find(father[x]);
        father[x]=tmp;
        return tmp;
    }
}
void Union(int a,int b){
    a=find(a);
    b=find(b);
    if(a!=b){
        if(height[a]<height[b]){
            father[a]=b;
        }else if(height[b]<height[a]){
            father[b]=a;
        }else{
            father[a]=b;
            height[b]++;
        }
    }
}

int main() {
    int n;
    while (cin >> n) { // 注意 while 处理多个 case
        if(n==0){break;}
        init(n);
        for(int i=0;i<n*(n-1)/2;i++){
            cin>>edge[i].from>>edge[i].to>>edge[i].cost>>edge[i].isBuild;
        }
        sort(edge,edge+n*(n-1)/2,cmp);
        int ans=0;
        for(int i=0;i<n*(n-1)/2;i++){
                int a=find(edge[i].to);
                int b=find(edge[i].from);
                if(a!=b){
                    Union(edge[i].to, edge[i].from);
                    if(!edge[i].isBuild){
                        ans+=edge[i].cost;
                    }
                }
        }
        cout<<ans<<endl;
    }
}
// 64 位输出请用 printf("%lld")
发表于 2024-04-01 13:32:33 回复(0)
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAX = 101;
struct Edge{
    int from;
    int to;
    int cost;
    bool operator< (const Edge a) const {
        return cost < a.cost;
    }
};
int father[MAX];
int height[MAX];
vector<Edge> edge0, edge1;
void Initial(){
    for(int i=0; i<MAX; ++i){
        father[i] = i;
        height[i] = 0;
    }
    return;
}
int Find(int x){
    if(x!=father[x]) return Find(father[x]);
    else return x;
}
void Union(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x != y){
        if(height[x] > height[y]){
            father[y] = x;
        }else if(height[x] < height[y]){
            father[x] = y;
        }else{
            father[x] = y;
            height[y]++;
        }
    }
    return;
}
int main(){
    int n;
    while (cin >> n && n!=0){
        Initial();
        int edgenum = n*(n-1)/2;
        for(int i=0; i<edgenum; ++i){
            Edge temp;
            int status;
            cin >> temp.from >> temp.to >> temp.cost >> status;
            if(status == 0) edge0.push_back(temp);
            else edge1.push_back(temp);
        }
        sort(edge0.begin(), edge0.end());
        int ans = 0;
        for(int i=0; i<edge1.size(); ++i){
            if(Find(edge1[i].from) != Find(edge1[i].to)) Union(edge1[i].from, edge1[i].to);
        }
        for(int i=0; i<edge0.size(); ++i){
            if(Find(edge0[i].from) != Find(edge0[i].to)){
                Union(edge0[i].from, edge0[i].to);
                ans+=edge0[i].cost;
            }
        }
        cout << ans << endl;
    }
}

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAX = 101;
struct Edge{
    int from;
    int to;
    int cost;
    bool operator< (const Edge a) const {
        return cost < a.cost;
    }
};
int father[MAX];
int height[MAX];
vector<Edge> edge0, edge1;
void Initial(){
    for(int i=0; i<MAX; ++i){
        father[i] = i;
        height[i] = 0;
    }
    return;
}
int Find(int x){
    if(x!=father[x]) return Find(father[x]);
    else return x;
}
void Union(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x != y){
        if(height[x] > height[y]){
            father[y] = x;
        }else if(height[x] < height[y]){
            father[x] = y;
        }else{
            father[x] = y;
            height[y]++;
        }
    }
    return;
}
int main(){
    int n;
    while (cin >> n && n!=0){
        Initial();
        int edgenum = n*(n-1)/2;
        for(int i=0; i<edgenum; ++i){
            Edge temp;
            int status;
            cin >> temp.from >> temp.to >> temp.cost >> status;
            if(status == 0) edge0.push_back(temp);
            else edge1.push_back(temp);
        }
        sort(edge0.begin(), edge0.end());
        int ans = 0;
        for(int i=0; i<edge1.size(); ++i){
            if(Find(edge1[i].from) != Find(edge1[i].to)) Union(edge1[i].from, edge1[i].to);
        }
        for(int i=0; i<edge0.size(); ++i){
            if(Find(edge0[i].from) != Find(edge0[i].to)){
                Union(edge0[i].from, edge0[i].to);
                ans+=edge0[i].cost;
            }
        }
        cout << ans << endl;
    }
}

编辑于 2024-03-25 00:32:16 回复(0)
#include <stdio.h>
#include <stdlib.h>

struct Edge {
    int from;
    int to;
    int cost;
    int build;
};

int father[100];
struct Edge e[100 * 100];
int height[100];

void initial(int n) {
    for (int i = 1; i <= n; i++) {
        father[i] = i;
        height[i] = 0;
    }
}



int Find(int x) {
    if (x != father[x]) {
        father[x] = Find(father[x]);
    }
    return father[x];
}

void Union(int a, int b) {
    a = Find(a);
    b = Find(b);
    if (a != b) {
        if (height[a] < height[b])
            father[a] = b;
        else if (height[a] > height[b])
            father[b] = a;
        else {
            father[a] = b;
            height[b]++;
        }
    }
}

int cmp(const void* a, const void* b) {
    const struct Edge* a1 = (const struct Edge*)a;
    const struct Edge* b1 = (const struct Edge*)b;
    if (a1->cost > b1->cost)
        return 1;
    if (a1->cost < b1->cost)
        return -1;
    return 0;
}

void initialF(struct Edge e[], int m){
    for(int i = 0; i < m; i++){
        if (e[i].build == 1){
            Union(e[i].from, e[i].to);
        }
    }
}

int KrusKal(struct Edge e[], int m) {
    initialF(e, m);
    int ans = 0;
    qsort(e, m, sizeof(struct Edge), cmp);
    for (int i = 0; i < m; i++) {
        if (Find(e[i].to) != Find(e[i].from) && e[i].build == 0) {
            Union(e[i].to, e[i].from);
            ans += e[i].cost;
        }
    }
    return ans;
}

int main() {
    int n, m, count = 0, f, t, c, bui;
    while (scanf("%d", &n) != EOF) {
        if (n == 0) {
            break;
        }
        initial(n);
        m = n * (n - 1) / 2;
        for (int i = 0; i < m; i++) {
            scanf("%d %d %d %d", &f, &t, &c, &bui);
            e[i].to = t;
            e[i].from = f;
            e[i].cost = c;
            e[i].build = bui; // 初始化build值
        }
        // for (int i = 0; i < m; i++){
        //     printf("%d", e[i].build);
        // }
        int res = KrusKal(e, m);
        printf("%d\n", res);
    }
    return 0;
}

编辑于 2024-03-23 23:03:24 回复(0)
写克鲁斯卡尔的时候,把已经建好的路的花费设置为0更好。
发表于 2024-03-23 21:16:36 回复(0)
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 100;

struct Edge {
    int from;   //边的起点
    int to;     //边的终点
    int length; //边的长度
    bool operator<(const Edge& e)const {    //重载小于号
        return length < e.length;
    }
};

vector<Edge>edge(MAXN* MAXN);   //边集
vector<int>father(MAXN);        //父亲结点
vector<int>height(MAXN);        //结点高度

void Initial(int n) {               //初始化
    for (int i = 0; i <= n; i++) {
        father[i] = i;
        height[i] = 0;
    }
}

int Find(int x) {                       //查找根结点
    if (x != father[x]) {               //路径压缩
        father[x] = Find(father[x]);
    }
    return father[x];
}

void Union(int x, int y) {              //合并集合
    x = Find(x);
    y = Find(y);
    if (x != y) {
        //矮树作为高树的子树
        if (height[x] < height[y]) {
            father[x] = y;
        } else if (height[y] < height[x]) {
            father[y] = x;
        } else {
            father[y] = x;
            height[x]++;
        }
    }
}

int Kruskal(int n, int edgeNum) {   //最小生成树的克鲁斯卡尔算法
    Initial(n);
    sort(edge.begin(), edge.begin() + edgeNum); //边按权值排序
    int sum = 0;
    for (int i = 0; i < edgeNum; i++) {
        Edge current = edge[i];
        if (Find(current.from) != Find(current.to)) {
            Union(current.from, current.to);
            sum += current.length;
        }
    }
    return sum; //返回最小总长度
}

int main() {
    int n;
    while (cin >> n && n != 0) {
        Initial(n);
        int edgeNum = n * (n - 1) / 2;
        for (int i = 0; i < edgeNum; i++) {
            int status; //修建状态
            cin >> edge[i].from >> edge[i].to >> edge[i].length >> status;
            if (status == 1) {
                edge[i].length = 0;
            }
        }
        int answer = Kruskal(n, edgeNum);
        cout << answer << endl;
    }
    return 0;
}

发表于 2024-02-21 12:35:29 回复(0)