牛客寒假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;
}