(图论)最小生成树+入门题

数据结构真的重要!!!所以记入下来,以后留给学弟看!

最小生成树(普里姆算法—Prim算法)+(克鲁斯卡尔算法) 实在是觉得Kruskal算法。。。。

板子的话!等我写Kruskal的时候补上

板子

Prim持续等待更新!!!复杂度 O(n*n)
算法:最小生成树Kruskal 复杂度 O(E)

最小生成树裸题

题目传送门:POJ1251
题意:给定n,接下来n-1行,每一行表示这个树跟其他点相连的距离!!!然根本不需要考虑重边!!!直接裸题!!!

AC代码

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)
typedef long long int ll;

void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
using namespace std;
const int maxn = 10004;
int fa[maxn];

int Find(int x){
	if(fa[x] == x)
		return x;
	else
		return fa[x] = Find(fa[x]);
}
struct node{
	int a,b,val;
}pp[maxn];
bool cmp(node a, node b){
	return a.val < b.val;
}
void init(int n){
	for(int i = 0; i <= n+1; i++)
		fa[i] = i;
}
int main(){
	
	int n;
	while(cin>>n,n){
		if( n == 0 )
			break;
		init(n);
		char x,y;
		int m,mm;
		int cnt = 0, ans = 0;
		for(int i = 1; i < n; i++){
			cin>>x>>m;
			for(int j = 1; j <=m;j++){
				cin>>y>>mm;
				pp[cnt].val =mm;
				pp[cnt].a = x-'A'; 
				pp[cnt].b = y - 'A';
				cnt++; 
			}
		}
		sort(pp,pp+cnt,cmp);
		for(int i = 0; i < cnt; i++){
			int x = pp[i].a;
			int y = pp[i].b;
			if(Find(x) != Find(y)){
				fa[Find(x)] = Find(y);
				ans = ans + pp[i].val; 
			}
		}
		pff(ans);
	}
	return 0;
} 

最小生成树第一题

题目传送门:POJ1287
题意: n,m表示的就是n颗树,接下来m条边,然后套…
我的理解: 我感觉我不改做这个数据结构专题的!!!(⊙o⊙)…
这个题目吧!我开始想错了!我看了有重边!本来想去重边!后来我发现了!完全不需要!!还是菜!!继续刷吧!!!

AC代码

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)
typedef long long int ll;

void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
using namespace std;
const int maxn = 10004;
int fa[maxn];
int Find(int x){
	if(fa[x] == x)
		return x;
	else
		return fa[x] = Find(fa[x]);
}
struct node{
	int a,b,val;
}pp[maxn];
bool cmp(node a, node b){
	return a.val < b.val;
}
void init(int n){
	for(int i = 0; i <= n+1; i++)
		fa[i] = i;
}
void add(int a,int b,int val,int cnt){
	pp[cnt].a = a;
	pp[cnt].b = b;
	pp[cnt].val = val;
}

int main(){
	
	int n, m, cc, ans, cnt, x, y, val;
	while(sa(n) && n){
		sa(m);
		init(n);
		cc = 0, ans = 0, cnt = 0;
		for(int i = 0; i < m; i++){
			sa(x),sa(y),sa(val);
			add(x,y,val,cnt++);
		}

		sort(pp, pp+cnt, cmp);
		for(int i = 0; i 

最小生成树第二题

题目传送门:POJ2031
题意: 告诉你n个球!然后的话,接下来n行表示的就是这个球空间坐标以及这个半径!然后这个题目需要你做的就是实现n个球相互连起来!你可以去搭建桥梁!求最小的长度!
我的理解: 一开始想了好久!然后想了下!可以直接用并查集!然后就是去考虑这个怎么去建边(建树)的过程了!仔细想想其实很简单!就是自己去判断这个两个球的位置关系!然后突然想两个都行下!!!

AC代码(kruskal)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)
using namespace std;


struct node{
	double x, y, z, r;
};	// pp[maxn]

struct edge{
	int from, to;
	double dis;
};	// e[maxn]

const int maxn = 105;
const double eps = 1e-10; 
int fa[maxn];
edge e[maxn*maxn];				//表示的就是边 
node pp[maxn];
int n,m;		// n 是点 m是边 

double dis(node v1,node v2){			//算空间的距离 
	
	double tmp;
	tmp = sqrt(  (v1.x-v2.x)*(v1.x-v2.x) + (v1.y-v2.y)*(v1.y-v2.y) + 
				(v1.z-v2.z)*(v1.z-v2.z) ) - v1.r - v2.r;
	if( tmp < eps )
		return 0.0;
	else
		return tmp;	
	
}
int Find(int x){
	return x == fa[x] ? x : fa[x] = Find(fa[x]); 
}
bool merge(int a, int b){			// 返回为真表示的就是不需要加边 
				
	int x = Find(a);
	int y = Find(b);
	if( x == y ){
		return true;
	}else{
		fa[x] = y;
	}
	return false;
}
bool cmp(edge a, edge b){		//重写排序 
	
	return a.dis < b.dis;
} 
void Kruskal( ){
	
	for(int i = 1; i <= n; i++)
		fa[i] = i;
	sort(e,e+m,cmp);
	double ans = 0.0;
	for(int i = 0; i < m; i++){
		int x = Find(e[i].from);
		int y = Find(e[i].to);
		if(!merge(x,y))			//	表示需要加答案
			ans += e[i].dis; 
	}
	printf("%.3f\n",ans); 		//这里是坑啊 
}	


int main(){
    
    while(sa(n)&&n){
    	if(n == 0)
    		break;
    	m = 0;
    	for(int i = 1; i <=n; i++ ){
    		scanf("%lf%lf%lf%lf",&pp[i].x,&pp[i].y,&pp[i].z,&pp[i].r);
    		for(int  j = 1; j < i; j++){
    			e[m].from = j;
    			e[m].to = i;
    			e[m++].dis = dis(pp[i],pp[j]);	//求的就是两个点的距离 
			}
		}
		Kruskal( );
	}
    return 0;
}

AC代码(Prim)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)

using namespace std;
struct node{
	double x, y, z, r;
};	// pp[maxn]

struct edge{
	int from, to;
	double dis;
};	

const int maxn = 105;
const double inf = 4000;
const double eps = 1e-10; 

double tree[maxn][maxn];
double dis[maxn];
edge e[maxn*maxn];				//表示的就是边 
node pp[maxn];
int n,m;		// n 是点 m是边 
int vis[maxn];

double dist(node v1,node v2){			//算空间的距离 
	
	double tmp;
	tmp = sqrt(  (v1.x-v2.x)*(v1.x-v2.x) + (v1.y-v2.y)*(v1.y-v2.y) + 
				(v1.z-v2.z)*(v1.z-v2.z) ) - v1.r - v2.r;
	if( tmp < eps )
		return 0.0;
	else
		return tmp;	
	
}
double Prime()
{
    double ans = 0;
    for(int i = 1; i <= n; i++) dis[i] = inf;
    dis[1] = 0; /** 第一个点入连通分量*/
 
    memset(vis, 0, sizeof(vis));
    
    for(int i = 1; i <= n; i++)
    {
        int x;
        double m = inf; /** 不断的找下一个距离连通分量最小的点*/
        for(int y = 1; y <= n; y++) if(!vis[y] && dis[y] <= m) m = dis[x=y];
        vis[x] = 1; /** 标记进入连通分量*/
        ans += dis[x]; /** 加入总路径 */
        for(int y = 1; y <= n; y++) if(!vis[y]) /**不断更新剩下未加入连通分量的点与连通分量的最短距离*/
            dis[y] = min(dis[y], tree[x][y]);
    }
    
    return ans;
}

void solve(){		//make_tree
	
	for(int i = 1 ; i <=n; i++)
		for(int j = 1 ; j <=n; j++)
			tree[i][j] =inf;
	for(int i = 1; i <= n; i++)
		scanf("%lf%lf%lf%lf",&pp[i].x,&pp[i].y,&pp[i].z,&pp[i].r);
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			if(i == j)	tree[i][j] = 0;
			else		tree[i][j] =dist(pp[i],pp[j]);
			tree[j][i] = tree[i][j]; 
		}
	}
}
int main(){
	
	while(scanf("%d",&n)&&n){
		if(n == 0 )
			break;
		
		solve();
		printf("%.3f\n",Prime());
	}
	return 0;
}

最小生成树第三题

题目传送门:POJ2421
题意: 给你一个矩阵!题意就是最小生成树!然后m条路已经相连起来!这题很容易RE
我的理解: 注意的地方就是你单组输入!!!

AC代码(Prime)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)

using namespace std;
struct node{
	double x, y, z, r;
};	// pp[maxn]

struct edge{
	int from, to;
	double dis;
};	

const int maxn = 105;
const int inf = 0x3f3f3f3f;
const double eps = 1e-10; 

int tree[maxn][maxn];
int dis[maxn];
edge e[maxn*maxn];				//表示的就是边 
node pp[maxn];
int n,m;		// n 是点 m是边 
int vis[maxn];

double dist(node v1,node v2){			//算空间的距离 
	
	double tmp;
	tmp = sqrt(  (v1.x-v2.x)*(v1.x-v2.x) + (v1.y-v2.y)*(v1.y-v2.y) + 
				(v1.z-v2.z)*(v1.z-v2.z) ) - v1.r - v2.r;
	if( tmp < eps )
		return 0.0;
	else
		return tmp;	
	
}

int Prime()
{
    int ans = 0;
    for(int i = 1; i <= n; i++) dis[i] = inf;
    dis[1] = 0; /** 第一个点入连通分量*/
	
    memset(vis, 0, sizeof(vis));
    
    for(int i = 1; i <= n; i++)
    {
        int x = 0;
        int m = inf; /** 不断的找下一个距离连通分量最小的点*/
        for(int y = 1; y <= n; y++) if(!vis[y] && dis[y] <= m)	m = dis[ x = y];
			 
        vis[x] = 1; /** 标记进入连通分量*/
        ans += m; /** 加入总路径 */
        for(int y = 1; y <= n; y++) 
			if(!vis[y]&&dis[y]>tree[x][y] ) /**不断更新剩下未加入连通分量的点与连通分量的最短距离*/
            	dis[y] = tree[x][y];
    }
    return ans;
}

void solve(){		//make_tree
	 
	int	x;  
	for(int i = 1 ; i <=n; i++)
		for(int j = 1 ; j <=n; j++){
			scanf("%d",&x);
			tree[i][j] =x;
		}
			
//	for(int i = 1; i <= n; i++)
//		scanf("%lf%lf%lf%lf",&pp[i].x,&pp[i].y,&pp[i].z,&pp[i].r);
	int m;
	scanf("%d",&m);
	while(m--){
		int a,b;
		scanf("%d%d",&a,&b);
		tree[a][b] = 0;
		tree[b][a] = 0;
	}
	
}
int main(){
	
	scanf("%d",&n);
	solve();
	printf("%d\n",Prime());
	return 0;
}

最小生成树第四题

题目传送门:Hdu1875
题意: 题目意思就是n个岛,现在需要他们相互连起来,然后有些小岛不能连!!!最后判断下这个情况是不是能相互连
我的理解: Prim中要是下一个m连通分量还是inf的话!说明就是不能所有的相连

AC代码(Prime)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sa(t) scanf("%d",&t)
#define SA(t) scanf("%lld", %t)
#define PF(t) printf("%lld",t)
#define pf(t) printf("%d", t)
#define PFF(t) printf("%lld\n",t)
#define pff(t) printf("%d\n", t)

using namespace std;
struct node{
	int  x, y;
	double z, r;
};	// pp[maxn]

struct edge{
	int from, to;
	double dis;
};	

const int maxn = 105;
const double inf = 0x3f3f3f3f;
const double eps = 1e-10; 

double tree[maxn][maxn];
double dis[maxn];
edge e[maxn*maxn];				//表示的就是边 
node pp[maxn];
int n,m;		// n 是点 m是边 
int vis[maxn];

double Prime()
{
    double ans = 0.0;
    for(int i = 1; i <= n; i++) dis[i] = inf;
    dis[1] = 0.0; /** 第一个点入连通分量*/
	
    memset(vis, 0, sizeof(vis));
    
    for(int i = 1; i <= n; i++)
    {
        int x = 0;
        double m = inf; /** 不断的找下一个距离连通分量最小的点*/
        for(int y = 1; y <= n; y++) if(!vis[y] && dis[y] <= m)	m = dis[ x = y];
		if(m == inf)		//判断不能 
			return -1;	 
        vis[x] = 1; /** 标记进入连通分量*/
        ans += m; /** 加入总路径 */
        for(int y = 1; y <= n; y++) 
			if(!vis[y]&&dis[y]>tree[x][y] ) /**不断更新剩下未加入连通分量的点与连通分量的最短距离*/
            	dis[y] = tree[x][y];
    }
    return ans;
}

void solve(){		//make_tree
	 
	int	x; 
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			tree[i][j]=inf,tree[j][i]=inf;
			
	for(int i = 1; i <= n; i++)
		scanf("%d%d",&pp[i].x,&pp[i].y); 
	for(int i = 1 ; i <=n; i++)
		for(int j = 1 ; j <=n; j++){
			
			double tt = sqrt((pp[i].x-pp[j].x)*(pp[i].x-pp[j].x)+(pp[i].y-pp[j].y)*(pp[i].y-pp[j].y));
			if(tt<=1000.0&&tt>=10.0){
				tree[i][j] = tt;
				
				tree[j][i] = tree[i][j];
			}
		}	
//	for(int i = 1; i <= n; i++)
//		scanf("%lf%lf%lf%lf",&pp[i].x,&pp[i].y,&pp[i].z,&pp[i].r);
		
}
int main(){
	
	
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		solve();
		double xx = Prime();
		if(xx == -1){
			printf("oh!\n");
		}
		else
		printf("%.1lf\n",xx*100);
	} 
	
	return 0;
}

代码持续更新!!!

你可能感兴趣的:(Acm算法)