测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。 当N为0时输入结束。
每个测试用例的输出占一行,输出全省畅通需要的最低成本。
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
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]; } } }
/** 这题使用了并查集的思想,造了最小生成树,因为事先按花费排序,所以这棵树一旦生成,就是代价最小 **/ #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; } }
#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; }
/* * *克鲁斯卡尔最小生成树,重点是边的排序,这里是否建立优先,其次是花费(能不花就不花)。 *但是,是否已经建立的情况下,花费的排序就多余了,可以再增加一个距离(花费优先)。 */ #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; }
#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; }
#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; }
#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; }
#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; }
在输入时就对已建好的路进行树的生成,且不保存,而后只保存未开始修建的路,然后进行排序和树的继续生成。
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
#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; }
#include <cstdio> #include <iostream> #include<vector> #include<algorithm> using namespace std; int father[10001]; void InitSet(int n) { for (int i = 0; i < n; i++) { father[i] = i; } } int FindFather(int u) { if (u == father[u]) { return u; } else { father[u] = FindFather(father[u]); return father[u]; } } void UnionSet(int r, int t) { r = FindFather(r); t = FindFather(t); father[r] = t; } struct edge { int u;//边的两个结点、权值 int v; int w; int live;//表示是否修建 edge(int _u, int _v, int _w, int _live) { u = _u; v = _v; w = _w; live = _live; } }; bool cmp(edge a, edge b) { if (a.live != b.live) { return a.live > b.live; //不花费钱的优先级最高先加入到并查集,从小到大最小生成树 } else { return a.w < b.w; } } int main() { int n; while (scanf("%d", &n) != EOF) { vector<edge> edgev;//存储边的向量 if (0 == n) { break; } for (int i = 0; i < (n - 1)*n / 2; i++) { int u, v, w, live; scanf("%d %d %d %d", &u, &v, &w, &live); edge e(u, v, w, live); edgev.push_back(e); } InitSet(10001); sort(edgev.begin(), edgev.end(), cmp); int edgenum = 0, totaledgesum = 0; //边的数量与权值之和 for (int i = 0; i < edgev.size(); i++) { int u = edgev[i].u, v = edgev[i].v, w = edgev[i].w, live = edgev[i].live; if (1 == live) { if (FindFather(u) != FindFather(v)) { edgenum++; //只有不在同一个并查集的已有路才算边 } UnionSet(u, v); } else { if (FindFather(u) != FindFather(v)) { edgenum++; UnionSet(u, v); totaledgesum += w; } } if (edgenum == n - 1) { //最小树已经生成完毕 break; } } printf("%d\n", totaledgesum); } } // 64 位输出请用 printf("%lld")
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int MAXN = 100; struct Edge { int form; int to; int length; }; int father[MAXN]; int height[MAXN]; vector<Edge> edge1(MAXN* MAXN); bool operator < (Edge lhs, Edge rhs) { return lhs.length < rhs.length; } void Init(int n) { for (int i = 0; i < n; ++i) { father[i] = i; height[i] = 0; } } int Find(int u) { if (father[u] == u) { return u; } else { father[u] = Find(father[u]); } return father[u]; } void Union(int u, int v) { int uroot = Find(u); int vroot = Find(v); if (uroot != vroot) { if (height[u] < height[v]) { father[uroot] = vroot; } else if (height[u] > height[v]) { father[vroot] = uroot; } else { father[vroot] = uroot; height[u]++; } } } int Kruskal(int n, int edgeNumber) { Init(n); sort(edge1.begin(), edge1.begin() + edgeNumber); int sum = 0; for (int i = 0; i < edgeNumber; ++i) { Edge current = edge1[i]; if (Find(current.form) != Find(current.to)) { Union(current.form, current.to); sum += current.length; } } return sum; } int main() { int n; while (cin >> n) { if (n == 0) { break; } int edgeNumber = n * (n - 1) / 2; int statue; for (int i = 0; i < edgeNumber; ++i) { cin >> edge1[i].form >> edge1[i].to >> edge1[i].length >> statue; if (statue == 1) { edge1[i].length = 0; } } int sum = Kruskal(n, edgeNumber); cout << sum << endl; } return 0; }
#include <iostream> #include <vector> #include <algorithm> using namespace std; struct road{ int townA; int townB; int cost; bool build; }; bool compare(road lhs,road rhs){ return lhs.cost<rhs.cost; } int findset(vector<int> &vec,int a){ while(vec[vec[a]]!=vec[a]){//路径压缩,如果a父亲不是根结点,则无限循环直到找到根结点 vec[a]=vec[vec[a]]; } return vec[a]; } void unionset(vector<int> &vec, int a, int b) { int rootA = findset(vec, a); int rootB = findset(vec, b); if (rootA != rootB) { vec[rootA] = rootB; } } int main(){ int n;//n表示村庄数目 while(cin >> n && n!=0){ vector<road> vec(n*(n-1)/2+1);//存放道路情况 for(int i=1;i<=(n*(n-1)/2);i++){//输入道路情况 cin >> vec[i].townA >> vec[i].townB >> vec[i].cost >> vec[i].build; } vector<int> connected_set(n+1);//并查集 for(int i=1;i<=n;i++){ connected_set[i]=i;//初始化,每个集合元素的父结点都是自己 } //kruskal算法 sort(vec.begin()+1,vec.end(),compare);//把所有道路按照从小到大排序 for(int i=1;i<=n*(n-1)/2;i++){ if(vec[i].build==true){//将已有道路的连接到并查集 unionset(connected_set,vec[i].townA,vec[i].townB); } } int need=-1;//还需修建need条路 for(int i=1;i<=n;i++){ if(findset(connected_set,i)==i){ need++; } } int money=0; for(int i=1;i<=n*(n-1)/2 && need>0;i++){ //如果没有修建过且未连通,则修建 if(!vec[i].build && (findset(connected_set,vec[i].townA)!=findset(connected_set,vec[i].townB))){ vec[i].build=true; unionset(connected_set,vec[i].townA,vec[i].townB); money+=vec[i].cost; need--; } } cout << money << endl; } }
#include<iostream> #include<algorithm> #include<vector> using namespace std; int village[100]; struct edge{ int start; int end; int fare; int flag; }; void Initset(int n){ for(int i=0;i<n;i++){ village[i]=i; } } bool compare(edge lhs,edge rhs){ if(lhs.flag>rhs.flag){ return true; } else if(lhs.flag==rhs.flag&&(lhs.fare<rhs.fare)){ return true; } else return false; } int findfather(int n){ if(village[n]==n){ return n; } else return findfather(village[n]); } int main(){ int n; while(cin>>n) { if(n==0){ break; } vector<edge> v(n * (n - 1 / 2)); // int village[n]; Initset(n + 1);//初始化并查集 for (int i = 0; i < n * (n - 1) / 2; i++) { cin >> v[i].start >> v[i].end >> v[i].fare >> v[i].flag; } sort(v.begin(), v.end(), compare); vector<edge>::iterator it; int weight = 0; int setnode = n; for (it = v.begin(); it != v.end(); it++) { int a = (*it).start; int b = (*it).end; int aroot = findfather(a); int broot = findfather(b); if (aroot != broot) { village[broot] = aroot; setnode--; if ((*it).flag == 0) { weight += (*it).fare; } if (setnode == 1) { break; } } } cout << weight << endl; } return 0; }