一本通->提高篇->圖論-> 最小生成樹:

一本通:
提高篇:
圖論:
最小生成樹:

1486 【例題1】黑暗城堡
求最短路徑生成樹,用最短路算法求出最短路後,找到最後dis數組,遍歷每條邊上找到對應成立dis[]的度數(個數),再用乘法原理求出結果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//const ll mod = (1<<31)-1;
#define mod 2147483647LL
const int N = 1005;
const int INF = 1e9;
const int M = 2000005;
int n,m,cnt;
int head[N],ver[M],nex[M],edge[M];
int vis[N];ll dis[N],ksum[N];
void add(int a,int b,int w){
	ver[++cnt] = b;
	nex[cnt] = head[a];
	edge[cnt] = w;
	head[a] = cnt;
}
void solve(){
	for(int i=0;i<=n;i++)
	dis[i] =INF;
	priority_queue<pair<int,int> > q;
	q.push(make_pair(0,1)),dis[1]=0;
	while(q.size()){
		int u =q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i=head[u];~i;i = nex[i]){
			int v=ver[i],w =edge[i];
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}
int work(){
	ll ans= 1;
	for(int i=1;i<=n;i++){
		for(int j=head[i];~j;j=nex[j])
		if(dis[i]+edge[j]==dis[ver[j]])
		ksum[ver[j]]++; //這是入度 ,表示這個點可以由多少最短路上的點得到 
	}
	for(int i=2;i<=n;i++)
	ans = ans*ksum[i] %mod;//根據乘法原理累計 
	printf("%lld",ans);
}
int main(){
	memset(head,-1,sizeof head);
	scanf("%d%d",&n,&m);
	for(int i = 1,x,y,w;i <= m;i++){
		scanf("%d%d%d",&x,&y,&w);
		add(x,y,w);
		add(y,x,w);
	}
	solve();
	work();
	return 0;
} 

1487 【例 2】北極通訊網絡
求最小生成樹的第k長邊

//求最小生成樹的第k長邊 
#include<bits/stdc++.h>
using namespace std;
const int N = 250100;
int n,k,m,cnt;
double ans[N];
int pre[N],x[N],y[N];
struct EG{
	int from,to;
	double w;
	EG(){}
	EG(int from,int to,double w) : from(from),to(to),w(w){}
	bool operator < (const EG &b) const{
		return (w - b.w) < 1e-9;
	};
}e[N];
void read(){
	scanf("%d%d",&n,&k);
	for(int i = 1,a,b;i <= n;i++){
		scanf("%d%d",x+i,y+i);
		pre[i] = i;
	}
}
int find(int x){
	return x==pre[x]?x:pre[x]=find(pre[x]);
}
void un(int x,int y){
	int fx = find(x);
	int fy = find(y);
	if(fx != fy) pre[fx] = pre[fy];
}
double dis(int a,int b){
	return sqrt( (x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b]) * (y[a]-y[b]) );
}
void solve(){
	for(int i = 1; i <= n; i++){
		for(int j=1+i;j <= n; j ++){
			e[++m] = EG(i,j,dis(i,j));
		}
	}
	sort(e+1,e+m+1);
	for(int i=1;i <= m; i++){
		int fx = find(e[i].from),fy = find(e[i].to);
		if(fx==fy) continue;
		un(fx,fy);
		ans[++cnt] = e[i].w;
		//cout<<ans[cnt-1]<<endl;
		if(cnt == n) break;
	}
	printf("%.2f",ans[cnt-k+1]);
}
int main(){
	read();
	solve();
	return 0;
} 

1488 新的開始
求最小生成樹

#include<bits/stdc++.h>
using namespace std;
const int N = 500,INF = 0x3f3f3f3f;
int n,ans,minn;
int c[N],w[N][N],dis[N],vis[N];
void read(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&w[0][i]),w[i][0] = w[0][i];
	for(int i=1;i <= n;i++)
	for(int j=1;j <= n;j++)
	scanf("%d",&w[i][j]);
}
void prim(){//堆優化的prim 
	for(int i=1;i<=n;i++) dis[i] = INF;
	priority_queue<pair<int,int> > q;
	dis[0] = 0;
	q.push(make_pair(0,0));
	int cnt = 0,ans=0;
	while(q.size() && cnt <=n){
		int d = -q.top().first,u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		cnt++;
		ans += d;
		vis[u] = 1;
		for(int i=1;i<=n;i++){
			if(w[u][i] < dis[i]){
				dis[i] = w[u][i];
				q.push(make_pair(-dis[i],i));
			}
		}
	}
	printf("%d",ans);
}
/*void prim(){
	for(int i=0;i<=n;i++) dis[i] = INF;
	dis[0] = 0,ans = 0;
	int k;
	for(int i=0;i<=n;i++){
		minn = INF;
		for(int j = 0;j<=n;j++)
		if(!vis[j] && minn > dis[j]) minn = dis[j],k=j;
		vis[k] = 1;
		ans += dis[k];
		for(int j=0;j<=n;j++)
		if(!vis[j] && dis[j] > w[k][j]) dis[j] = w[k][j];
	}
	printf("%d",ans);
}*/
int main(){
	read();
	prim();
	return 0;
} 

1489:構造完全圖
因爲沒有確切的邊,我們只要把最小生成樹上的點兩兩相連,這些非樹邊的權值是這兩個點權值+1。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7,INF = 0x3f3f3f3f;
typedef long long ll;
ll ans;
ll n,pre[N],num[N];
struct E{
	ll from,to,w;
	E(){}
	E(ll from,ll to,ll w):from(from),to(to),w(w){}
	bool operator < (const E &b) const{
		return w<b.w;
	} 
}e[N];
ll find(int x){
	return x==pre[x]?x:pre[x]=find(pre[x]);
}
void read(){
	scanf("%lld",&n);
	for(int i=1;i<n;i++)
	scanf("%lld%lld%lld",&e[i].from,&e[i].to,&e[i].w),ans+=e[i].w;
	for(int i=1;i<=n;i++)
	pre[i] = i,num[i] = 1;
}
void solve(){
	sort(e+1,e+n);
	for(int i=1;i<n;i++){
		int r1 = find(e[i].from);
		int r2 = find(e[i].to);
		if(r1!=r2){
			ans += (num[r1]*num[r2]-1)*(e[i].w+1); 
			pre[r1] = r2;
			num[r2] += num[r1];
		}
	}
	printf("%lld\n",ans);
}
int main(){
	read();
	solve();
	return 0;
} 

1490:祕密的牛奶運輸
求次小生成樹,次小生成樹講解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5+7,M = 3e5 + 7,MM = 3e5+7;
const ll INF = 0x7ffffffffff;;
int n,m;
ll sum;
int cnt,head[MM],ver[MM],nex[MM],edge[MM];
int tree[MM],pre[N],ppre[N][23],depth[N],lg[N];
ll maxf[N][23],minf[N][23];//這兩個要和ans 作比較,所以用ll 
struct E{
	int from,to,w;
	E(){}
	E(int from,int to,int w) : from(from),to(to),w(w){}
	bool operator < (const E &b)const{
		return w < b.w;
	}
}e[M];
void add(int x,int y,int w){
	 ver[++cnt] = y;
	 nex[cnt] = head[x];
	 edge[cnt] = w;
	 head[x] = cnt;
}
int find(int x){
	return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void read(){
	scanf("%d%d",&n,&m);
	for(int i=1;i <= m;i ++)
		scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w);
	
	for(int i=0; i < N; i++)
	pre[i] = i;
}
void work1(){
	sort(e+1,e+m+1);
	for(int i=1;i <= m;i ++){
		int x = e[i].from,y = e[i].to,w = e[i].w;
		int fx = find(x), fy = find(y);
		if(fx != fy){
			pre[fx] = fy;
			sum += w;
			add(x,y,w);
			add(y,x,w);
			tree[i] = 1;
		}
	}
}
void dfs(int f,int fa,int w){
	depth[f] = depth[fa] + 1;
	ppre[f][0] = fa;
	minf[f][0] = -INF;
	maxf[f][0] = w;
	for(int i=1; (1<<i) <= depth[f];i++){
		ppre[f][i] = ppre[ppre[f][i-1]][i-1];
		maxf[f][i] = max(maxf[f][i-1],maxf[ppre[f][i-1]][i-1]);
		minf[f][i] = max(minf[f][i-1],minf[ppre[f][i-1]][i-1]);//這裏分清次小關係 
		if(maxf[f][i-1] > maxf[ppre[f][i-1]][i-1]) minf[f][i]  = max(minf[f][i],maxf[ppre[f][i-1]][i-1]);
		else if(maxf[f][i-1] < maxf[ppre[f][i-1]][i-1]) minf[f][i] = max(minf[f][i],maxf[f][i-1]);
	}
	
	for(int i=head[f]; i ; i=nex[i]){
		int y = ver[i],w = edge[i];
		if(y != fa){
			dfs(y,f,w);
		}
	}
}
int lca(int x,int y){
	if(depth[x] < depth[y]) swap(x,y);
	while(depth[x] > depth[y]) 
	x = ppre[x][lg[depth[x]-depth[y]] -1];
	
	if(x==y) return x;
	for(int i = lg[depth[x]]-1; i>=0; i--){
		if(ppre[x][i] != ppre[y][i]) 
		x= ppre[x][i],y = ppre[y][i];
	}
		
	return ppre[x][0];
}
ll qmax(int x,int y,int maxx){
	ll ans = -INF;
	
	for(int i = lg[depth[x]]-1;i>=0;i--){
		if(depth[ppre[x][i]]>=depth[y]){
			if(maxx != maxf[x][i]) ans = max(ans,maxf[x][i]);
			else ans = max(ans,minf[x][i]);
			x = ppre[x][i];
		}
	}
	return ans;
} 
void work2(){
	for(int i=1;i <= n;i++)
	lg[i] = lg[i-1] + (1<<lg[i-1]==i);

	dfs(1,0,0);
	ll ans = INF;
	for(int i = 1;i <= m;i++){
		if(tree[i]) continue;
		int x = e[i].from,y = e[i].to,w = e[i].w;
		int lc = lca(x,y);
		ll maxx = qmax(x,lc,w);
		ll maxv = qmax(y,lc,w);
		ans = min(ans,sum-max(maxx,maxv)+w);//找到到公共父親節點路徑中最大的那條,替換到非樹邊。 
	}
	printf("%lld\n",ans);
}
int main(){
	read();
	work1();
	work2();
	return 0;
} 

1491:Tree
聽說數據有問題還是咋地

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+7,M = 2e5+7;
typedef long long ll;
struct E{
	int from,to,w,col;
	bool operator < (const E &b) const{
		if(w==b.w) return col < b.col;//這是一個坑點,排序的時候如果值相等,要優先考慮白的 
		return w < b.w;
	}
}e[M];
int zong,n,m,need;
int pre[N];
ll ans,res;
void read(){
	scanf("%d%d%d",&n,&m,&need);
	for(int i=1;i <= m;i++){
		scanf("%d%d%d%d",&e[i].from,&e[i].to,&e[i].w,&e[i].col);
	}
}
int find(int x){
	return x == pre[x] ? x : pre[x] = find(pre[x]); 
}
void kuskr(int mid){
	for(int i=0;i<=n;i++)
	pre[i] = i;
	
	ans = zong = 0;
	for(int i=1;i<=m;i++)
	if(!e[i].col) e[i].w += mid;
	
	sort(e+1,e+m+1);
	for(int i=1;i <= m;i++){
		int fx = find(e[i].from);
		int fy = find(e[i].to);
		if(fx != fy){
			pre[fx] = fy;
			ans += e[i].w;
			if(!e[i].col) zong ++;
		}
	}
	
	for(int i=1;i<=m;i++)
	if(!e[i].col) e[i].w -= mid;
}
void solve(){
	int l =-100,r=100;
	while(l <= r){
		int mid = (l+r)>>1;
		kuskr(mid);
		if(zong >= need) l=mid+1,res = ans - need*mid;//這個點我有點不明白,爲什麼是need而不是zong,比較是樹上的,即使最後保證有結果。 
		else r = mid-1; 
	}
	printf("%lld\n",res);
}
int main(){
	read();
	solve();
	return 0;
} 

1492:最小生成樹計數
矩陣樹後碼,這裏給出的是暴力求。

#include<bits/stdc++.h>
using namespace std;
const int N = 1100,M = 1e3+7,mod = 31011;
int n,m,tot,cnt;
int ans,sum;
int l[N],r[N],va[N],pre[N];
struct E{
	int from,to,w;
	bool operator < (const E &b) const{
		return w < b.w;
	} 
}e[M];
int find(int x){
	return x==pre[x] ? x: find(pre[x]);
}
void read(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w);
} 
void dfs(int x,int now,int k){
	if(now == r[x]+1){
		if(k == va[x]) sum++;
		return;
	}
	int fx = find(e[now].from),fy = find(e[now].to);
	if(fx != fy){
		pre[fx] = fy;
		dfs(x,now+1,k+1);
		pre[fx] = fx,pre[fy] = fy;
	}
	dfs(x,now+1,k);
}
void solve(){
	sort(e+1,e+m+1);
	for(int i=0;i<=110;i++) pre[i] = i;
	for(int i=1;i <= m;i++){
		if(e[i].w != e[i-1].w) cnt++,l[cnt]=i,r[cnt-1]=i-1;
		int fx = find(e[i].from),fy = find(e[i].to);
		if(fx != fy){
			pre[fx] = fy;
			va[cnt]++; tot++;
		}
	}
	if(tot != n-1){
		printf("0");return;
	}
	r[cnt] = m;
	for(int i=0;i<=110;i++) pre[i] = i;
	ans = 1;
	for(int i=1;i <= cnt; i++){
		sum = 0;
		dfs(i,l[i],0);
		ans = (ans*sum) % mod;
		for(int j=l[i];j<=r[i];j++){
			int fx = find(e[j].from),fy = find(e[j].to);
			if(fx != fy) pre[fx] = fy;
		}
	}
	printf("%d",ans);
}
int main(){
	read();
	solve();
	return 0;
} 

1493:次小生成樹
參考 1490 祕密的牛奶運輸,代碼一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章