畅通工程系列题解

目录

HDU 1232 畅通工程 【传送门】

HDU 1233 还是畅通工程 【传送门】

HDU 1863 畅通工程 【传送门】

HDU 1874 畅通工程续 【传送门】

HDU 1875 畅通工程再续 【传送门】

HDU 1879 继续畅通工程 【传送门】


HDU 1232 畅通工程 【传送门】

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 82331    Accepted Submission(s): 43762

Problem Description

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

Input

测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最少还需要建设的道路数目。

Sample Input

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

Sample Output

1
0
2
998

Hint

Hint Huge input, scanf is recommended.

解题思路:并查集,查找祖先等于自身的节点的就可以了。

AC代码:

#include<iostream>

using namespace std;

int f[1005];

void init(int n)
{
	for(int i=1;i<=n;i++)
		f[i]=i;
}

int find(int a)
{
	while(a!=f[a])
	{
		a=f[a];
	}
	return a;
}

void Combin(int a,int b)
{
	int ta,tb;
	ta=find(a);
	tb=find(b);	
	if(ta!=tb)
		f[ta]=tb;
}

int answer(int n)
{
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		if(f[i]==i)
		{
			sum++;
		}
	}
	return sum;
}

int main()
{
	int n,m;
	while(1)
	{
		cin >> n;
		if(n==0)
			break;
		cin >> m;
		init(n);
		int a,b;
		for(int i=0;i<m;i++)
			cin >> a >> b,Combin(a,b);
		cout << answer(n)-1 << endl ;
	}
	return 0;
}

HDU 1233 还是畅通工程 【传送门】

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 69969    Accepted Submission(s): 31672

Problem Description

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最小的公路总长度。

Sample Input

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

Sample Output

3
5

Hint

Hint Huge input, scanf is recommended.

解题思路:毫无丝毫变动的最小生成树Prim算法模板题。

AC代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set> 
#include<algorithm>
using namespace std;
#define lowbit(x) (x&-x)
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define INF 999999
const int MAXN=105;

int cost[MAXN][MAXN];
int mincost[MAXN];
bool used[MAXN];
int V,E;
int res=0;
int prim(){
	mem(mincost,INF);
	mem(used,false);
	
	mincost[1]=0;
	
	while(true){
		int v=-1;
		//从不属于X的顶点中选取从X到其权值最小的顶点
		for(int u=1;u<=V;u++){
			if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
				v=u;
		} 
		if(v==-1) break;
		used[v]=true;
		res+=mincost[v];
		for(int u=1;u<=V;u++){
			mincost[u]=min(mincost[u],cost[v][u]);
		}
	}
	return res;
}

int main(){
	
	while(~scanf("%d",&V) && V){
		mem(cost,0);
		res=0;
		E=V*(V-1)/2;
		int a,b,c;
		for(int i=0;i<E;i++){
			scanf("%d%d%d",&a,&b,&c);
			cost[a][b]=cost[b][a]=c;
		}
		prim();
		printf("%d\n",res);
	}
	
	return 0;
}

HDU 1863 畅通工程 【传送门】

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 45888    Accepted Submission(s): 20519

Problem Description

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。

Input

测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。

Output

对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。

Sample Input

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

Sample Output

3
?

解题思路:最小生成树,克鲁斯卡尔模板直接套用,最后判断联立的节点有没有到达V-1个,如果没有到达V-1个,输出“?”

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define MAXN 1000

struct edge{
	int from,to;
	float cost;
};
edge es[MAXN];
int V,E;
int par[MAXN],Rank[MAXN];
int res;
int top;

void init(int n){
	for(int i=0;i<=n;i++){
		par[i]=i;
		Rank[i]=0;
	}
	res=0;
}

int find(int x){
	if(par[x]==x) return x;
	else return par[x]=find(par[x]);
}

void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return;
	if(Rank[x]<Rank[y]) 
		par[x]=y;
	else{
		par[y]=x;
		if(Rank[x]==Rank[y])
			Rank[x]++;
	}
}

bool same(int x,int y){
	return find(x)==find(y);
}

bool cmp(edge a,edge b){
	return a.cost<b.cost;
}

void kruskal(){
	init(top);
	sort(es+1,es+top+1,cmp);
	int cnt=0;
	for(int i=1;i<=top;i++){
//		cout << i <<endl;
		if(!same(es[i].from,es[i].to)){
			unite(es[i].from,es[i].to);
			res+=es[i].cost;
			cnt++;
//			cout << es[i].cost << endl;
		}
	}
	if(cnt<V-1){
		cout << "?\n";
	}
	else
		cout << res <<endl;
}

int main(){
	ios::sync_with_stdio(false);
	while(cin >> E >> V && E){
		int a,b,c;
		top=0;
		for(int i=1;i<=E;i++){
			cin >>a >> b >> c;
			es[++top].from=a,es[top].to=b,es[top].cost=c;
			es[++top].from=b,es[top].to=a,es[top].cost=c;
		}
		kruskal();
	}
	return 0;
}

HDU 1874 畅通工程续 【传送门】

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 82329    Accepted Submission(s): 31684

Problem Description

某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。

Input

本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。

Output

对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.

Sample Input

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

Sample Output

2
-1

解题思路:单源最短路径,迪杰斯特拉模板题,注意最后判断“-1”,必须是>=INF

AC代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<set> 
#include<algorithm>
using namespace std;
#define lowbit(x) (x&-x)
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
#define INF 999999
#define MAX_V 300
#define MAX_E 1050
const int MAXN=50001*4;

struct edge{
	int to,cost;
};
typedef pair<int,int> P;	//first最短距离、second是顶点的编号 
int V,E;
vector<edge> G[MAX_V];
int d[MAX_V];

void dijkstra(int s)
{
	priority_queue<P,vector<P>,greater<P> > que;
	mem(d,INF);
	d[s]=0;
	que.push(P(0,s));
	while(!que.empty())
	{
		P p=que.top();
		que.pop();
		int v=p.second;
		if(d[v]<p.first) continue;
		for(int i=0;i<G[v].size();i++)
		{
			edge e=G[v][i];
			if(d[e.to]>d[v]+e.cost)
			{
				d[e.to]=d[v]+e.cost;
				que.push(P(d[e.to],e.to));
			}
		}
	}
}

int main()
{
	
	ios::sync_with_stdio(false);
	while(cin >> V >> E)
	{
		for(int i=0;i<V;i++)
			G[i].clear();
		int a,b,c;
		for(int i=0;i<E;i++)
		{
			cin >> a >> b >> c;
			edge e;
			e.to=b,e.cost=c;
			G[a].push_back(e);
			e.to=a,e.cost=c;
			G[b].push_back(e);
		}
		cin >> a >> b;
		dijkstra(a);
//		for(int i=0;i<V;i++)
//			cout << d[i] << " " ;
//		cout << endl;
		if(d[b]<=INF)
			cout << d[b] << endl;
		else
			cout << "-1\n";
	}
	
	return 0;
}

HDU 1875 畅通工程再续 【传送门】

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 44877    Accepted Submission(s): 15120

Problem Description

相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。

Input

输入包括多组数据。输入首先包括一个整数T(T <= 200),代表有T组数据。
每组数据首先是一个整数C(C <= 100),代表小岛的个数,接下来是C组坐标,代表每个小岛的坐标,这些坐标都是 0 <= x, y <= 1000的整数。

Output

每组输入数据输出一行,代表建桥的最小花费,结果保留一位小数。如果无法实现工程以达到全部畅通,输出”oh!”.

Sample Input

2
2
10 10
20 20
3
1 1
2 2
1000 1000

Sample Output

1414.2
oh!

解题思路:最小生成树,克鲁斯卡尔算法,建图之后约束一下两个节点连接的条件即可。

注意!!浮点类型一定要用double!!!不然会WA的超级超级超级超级超级超级超级惨!!!!!!

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define MAXN 500000+5
struct Point{
	int x,y;
};
struct edge{
	int from,to;
	double cost;
};
edge es[MAXN];
Point p[MAXN];
int V,E;
int par[MAXN],Rank[MAXN];
double res;

void init(int n){
	for(int i=0;i<=n;i++){
		par[i]=i;
		Rank[i]=0;
	}
	res=0;
}

int find(int x){
	if(par[x]==x) return x;
	else return par[x]=find(par[x]);
}

void unite(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y) return;
	if(Rank[x]<Rank[y]) 
		par[x]=y;
	else{
		par[y]=x;
		if(Rank[x]==Rank[y])
			Rank[x]++;
	}
}

bool same(int x,int y){
	return find(x)==find(y);
}

bool cmp(edge a,edge b){
	return a.cost<b.cost;
}

void kruskal(){
	init(V);
	sort(es+1,es+E+1,cmp);
	int cnt=0;
	for(int i=1;i<=E;i++){
		if(cnt==V-1) break;
//		edge e=es[i];
//		cout << e.cost << " " <<e.to << " " <<e.to <<endl;
		if(same(es[i].from,es[i].to)||es[i].cost<10||es[i].cost>1000)
			continue;
		unite(es[i].from,es[i].to);
		res+=es[i].cost;
		++cnt;
	}
		
	if(cnt<V-1)
		printf("oh!\n");
	else
		printf("%.1f\n",res*100);
	//return res;
}

void deal(){
	double  tmp;
	for(int i=1;i<=V;i++){
		for(int j=i+1;j<=V;j++){
			tmp=abs(sqrt(1.0*(p[i].x-p[j].x)*(p[i].x-p[j].x)+1.0*(p[i].y-p[j].y)*(p[i].y-p[j].y)));
			es[++E].from=i;
			es[E].to=j;
			es[E].cost=tmp;
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	int T;
	scanf("%d",&T);
	while(T--){
		E=0;
		mem(es,0);
		scanf("%d",&V);
		for(int i=1;i<=V;i++){
			scanf("%d%d",&p[i].x,&p[i].y);	
		}
		deal();
		init(V);
		kruskal();
	}
	return 0;
}

HDU 1879 继续畅通工程 【传送门】

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 33398    Accepted Submission(s): 13893

Problem Description

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。

Input

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

当N为0时输入结束。

Output

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

Sample Input

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

Sample Output

3
1
0

解题思路:最小生成树,克鲁斯卡尔算法,标记为1的道路其cost=0,然后套模板,输出答案……

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define MAXN 1000
#define INF 9999999

int cost[MAXN][MAXN];
int mincost[MAXN];
bool used[MAXN];
int V,E;

void prim(){
	for(int i=1;i<=V;i++){
		mincost[i]=INF;
		used[i]=false;
	}
	mincost[1]=0;
	int res=0;
	while(true){
		int v=-1;
		for(int u=1;u<=V;u++){
			if(!used[u]&&(v==-1||mincost[u]<mincost[v]))
				v=u;
		}
		if(v==-1) break;
		used[v]=true;
		res+=mincost[v];
		for(int u=1;u<=V;u++){
			mincost[u]=min(mincost[u],cost[v][u]);
		}
	}
	cout << res << endl;
}

int main(){
	ios::sync_with_stdio(false);
	while(cin >> V && V){
		int a,b,c,d;
		E=(V*(V-1))/2;
		for(int i=1;i<=E;i++){
			cin >>a >> b >> c >> d;
			if(d==0){
				cost[a][b]=c;
				cost[b][a]=c;
			}else if(d==1){
				cost[a][b]=0;
				cost[b][a]=0;
			}
		}
		prim();
	}
	return 0;
}

OVER!!!

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值