洛谷 P2573 [SCOI2012]滑雪(有向图+prim最小生成树)

传送门

由于有向图的特殊性,因此不能每次简单判断最短边加入,否则可能出现以下情况:

洛谷 P2573 [SCOI2012]滑雪(有向图+prim最小生成树)_第1张图片
假设在上图中,在已经连通了 ( 1 , 3 , 4 ) (1,3,4) (1,3,4) 三个点的情况下,边 ( 2 , 4 ) (2,4) (2,4) 比边 ( 1 , 2 ) (1,2) (1,2) 更短,则此时将加入边 ( 2 , 4 ) (2,4) (2,4) ,形成上图。然而由于边的有向性(即题面中的高度限制),此时的点 2 2 2 其实是到达不了的,必须需要通过边 ( 1 , 2 ) (1,2) (1,2) 进行连通。因此对于有向图的最小生成树,在加入某条边时还需要判断该边的始点是否已经被加入

有关有向图的最小生成树,即最小树形图,其实可以通过朱刘算法解决(当然了,我不会 )。仔细阅读题面后发现,可以贪心的选择高度最高且路径最短的点加入,发现其他选择都不会比该种选法更优。

本题采用了 P r i m Prim Prim 算法,显然 K r u s k a l Kruskal Kruskal 算法也是可行的(如果以后有时间补上的话)

#include
#define int long long
using namespace std;

const int N=1e5+10;
const int M=2e6+10; 
const int INF=0x3f3f3f3f;
vector<pair<int,int>> h[N];

int ans1,ans2;
bool vis[N];
int dis[N],high[N];

struct Node{
	int numble,h,d;
	bool operator <(const Node &t) const{
		if(t.h!=h)
			return h<t.h;
		return d>t.d;
	}
}no;

priority_queue<Node> q;

void prim(){
	no.numble=1,no.h=high[1],no.d=0;
	dis[1]=0;
	q.push(no);
	while(!q.empty()){
		int x=q.top().numble;
		q.pop();
		if(vis[x])
			continue;
		if(dis[x]>=INF)
			return;
			
		ans1++;
		ans2+=dis[x];
		
		vis[x]=1;
		for(auto i:h[x]){
			int y=i.first;
			int val=i.second;
			if(dis[y]>val&&!vis[y]){
				dis[y]=val;
				no.numble=y,no.h=high[y],no.d=dis[y];
				q.push(no);
			}
		}
	}
}

signed main(){
	memset(dis,0x3f,sizeof(dis));
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) 
		cin>>high[i];
	int u,v,k;
	for(int i=1;i<=m;i++){
		cin>>u>>v>>k;
		
		//建边 
		if(high[u]>high[v])
			h[u].push_back({v,k});
		else if(high[v]>high[u])
			h[v].push_back({u,k});
		else{
			h[v].push_back({u,k});
			h[u].push_back({v,k});
		}
	} 
	prim();
	cout<<ans1<<" "<<ans2;
	return 0;
}

你可能感兴趣的:(图论,c++,算法)