牛客寒假6-I |最小生成树 + 重新建图 (+ floyd)

题目地址:https://ac.nowcoder.com/acm/contest/3007/I

思路

代码

代码1:最小生成树 + floyd

最小生成树的时候,保存n-1条边即答案。
floyd求点到点的最短距离,复杂度O(N^3)能过

#include<bits/stdc++.h>
using namespace std;

const int maxn= 610;
const int maxm = 1000000;

struct edge{
    int u,v,w;
    bool operator < (const edge &a) const{
        return w < a.w;
    }
}e[maxm];

struct node{
	int v,w;
};

vector<node> g[maxn];

int fa[maxn],n,m,len = 0;

int get(int x){
    if(fa[x] == x){
        return fa[x];
    }
    return fa[x] = get(fa[x]);
}

int dist[maxn],dis[maxn][maxn];
vector<int> ans;
int a[maxn][maxn];

void init(){
	memset(dis,0x3f3f3f3f,sizeof dis);
    for(int i = 1;i <= n;i++){
        dis[i][i] = 0;
    }
} 

void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
			}
		}
	}
}

int main() {
    cin>> n;
    len = 0;
    init();
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++) cin>>a[i][j];
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		if(i < j) e[len].u = i,e[len].v = j,e[len].w = a[i][j],len++;
		}
	}
    sort(e,e+len);
    for(int i=1;i<=n;i++) fa[i] = i;
    int sum = 0;
    for(int i=0;i<len;i++){
        int x = get(e[i].u), y = get(e[i].v);
        if(x != y){ //并查集求得最短边长 
            fa[x] = y;
            ans.push_back(e[i].w); //存答案 
            dis[e[i].u][e[i].v] = dis[e[i].v][e[i].u] = e[i].w; //记录新的邻接矩阵 
            sum += e[i].w;
        }
    }
    floyd(); //跑最短路 求出点到点的最短距离 500^3 
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		if(a[i][j] != dis[i][j]){
    			cout<<"No"<<endl;
    			return 0;
			}
		}
	}
	cout<<"Yes"<<endl; 
	//升序输出边长 
	sort(ans.begin(),ans.end());
	for(int i=0;i<ans.size();i++) cout<<ans[i]<<endl;
    return 0;
}

代码2:最小生成树 + dfs求各点到根距离 + 重新建图

最小生成树的时候使用邻接表重新建图。
dfs求各点到根的距离,就能判断是否合法,为什么?因为树上每个点到根的距离是唯一的,所以两点之间距离 = 点1到根 + 点2到根 也是唯一的。

#include<bits/stdc++.h>
using namespace std;

const int maxn= 610;
const int maxm = 1000000;

struct edge{
    int u,v,w;
    bool operator < (const edge &a) const{
        return w < a.w;
    }
}e[maxm];

struct node{
	int v,w;
};

vector<node> g[maxn];

int fa[maxn],n,m,len = 0;
int vis[maxn];

int get(int x){
    if(fa[x] == x){
        return fa[x];
    }
    return fa[x] = get(fa[x]);
}

int dist[maxn],dis[maxn][maxn];
vector<int> ans;

void dfs(int x,int fa,int dis){
	dist[x] = dis;
	for(int i=0;i<g[x].size();i++){
		int v = g[x][i].v;
		if(v != fa){
			int w = g[x][i].w;
			dfs(v,x,dis+w);
		}
	}
}

void dfs(int x,int dis){
	dist[x] = dis;
	vis[x] = 1;
	for(int i=0;i<g[x].size();i++){
		int v = g[x][i].v;
		if(!vis[v]){
			int w = g[x][i].w;
			dfs(v,dis+w);
		}
	}
}

int main() {
    cin>> n;
    len = 0;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		int w;
    		cin>>w;
    		if(i < j)
    			e[len].u = i,e[len].v = j,e[len].w = w,len++;
    		dis[i][j] = w;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(dis[i][j] != dis[j][i]){
				puts("No");
				return 0;
			}
		}
	}
    sort(e,e+len);
    for(int i=1;i<=n;i++){
        fa[i] = i;
    }
    int sum = 0;
    for(int i=0;i<len;i++){
        int x = get(e[i].u), y = get(e[i].v);
        if(x != y){
            fa[x] = y;
            ans.push_back(e[i].w); //存储最小生成树的n-1条最短边 
			g[e[i].u].push_back({e[i].v,e[i].w}); //存储新图的邻接表 
			g[e[i].v].push_back({e[i].u,e[i].w}); //存储新图的邻接表 
            sum += e[i].w;
        }
    }
    //找起点(根)
    int s = 1;
	for(int i=1;i<=n;i++){
		if(g[i].size() == 1){
			s = i;
			break;
		}
	} 
	//dfs统计距离 两个版本dfs都可 
	dfs(s,-1,0);  
//	dfs(s,0);
	//与输入的最短距离作比较 
	for(int i=1;i<=n;i++){
		if(dist[i] != dis[s][i]){
			cout<<"No"<<endl;
			return 0;
		}
	}
	cout<<"Yes"<<endl; 
	//升序输出边长 
	sort(ans.begin(),ans.end());
	for(int i=0;i<ans.size();i++) cout<<ans[i]<<endl;
    return 0;
}
posted @ 2020-02-19 11:24  fishers  阅读(254)  评论(0编辑  收藏  举报