Day3 生成樹專題

~hsm~發表於2019-02-13

A. UVALive 6437 Power Plant

題目

UVALive 6437

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct rec
{
	int x,y,z;
}edge[maxn*maxn];
int fa[maxn];
inline int get(int x)
{
	if (fa[x]==x) return x;
	return fa[x]=get(fa[x]);
}
inline bool cmp(rec a,rec b)
{
	return a.z<b.z;
}
int main()
{
	int t;read(t);
	for (int cnt=1;cnt<=t;++cnt)
	{
		int n,m,k,f,ans=0;
		read(n);read(m);read(k);
		for (int i=1;i<=n;++i)
			fa[i]=i;
		read(f);
		for (int i=2;i<=k;++i)
		{
			int x;read(x);
			fa[x]=f;
		}
		for (int i=1;i<=m;++i)
		{
			read(edge[i].x);
			read(edge[i].y);
			read(edge[i].z);
		}
		sort(edge+1,edge+m+1,cmp);
		for (int i=1;i<=m;++i)
		{
			int x=edge[i].x;
			int y=edge[i].y;
			if (get(x)==get(y)) continue;
			fa[fa[x]]=fa[y];
			ans+=edge[i].z;
		}
		printf("Case #%d: %d\n",cnt,ans);
	}
	return 0;
}

B. POJ 2349 UVA 10369 Arctic Network

題目

POJ 2349
LUOGU UVA10369

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct rec
{
	int fr,to;
	double z;
}edge[maxn*maxn];
int fa[maxn],dx[maxn],dy[maxn];
inline int get(int x)
{
	return fa[x]==x?x:fa[x]=get(fa[x]);
}
inline void combine(int x,int y)
{
	int xx=get(x),yy=get(y);
	if (xx!=yy) fa[xx]=yy;
}
inline bool cmp(rec a,rec b)
{
	return a.z<b.z;
}
int main()
{
	int t;read(t);
	while (t--)
	{
		int m,n,len=0,tot=0;
		double ans=0.00;
		read(m);read(n);
		for (int i=1;i<=n;++i)
		{
			fa[i]=i;
			read(dx[i]);read(dy[i]);
		}
		for (int i=1;i<n;++i)
			for (int j=i+1;j<=n;++j)
				edge[++len].fr=i,edge[len].to=j,
				edge[len].z=sqrt(double((dx[i]-dx[j])*(dx[i]-dx[j]))
								+double((dy[i]-dy[j])*(dy[i]-dy[j])));
		sort(edge+1,edge+len+1,cmp);
		for (int i=1;i<=len;++i)
		{
			if (get(fa[edge[i].fr])!=get(fa[edge[i].to]))
			{
				combine(edge[i].fr,edge[i].to);
				++tot;
				ans=edge[i].z;
			}
			if (tot==n-m) break;
		}
		printf("%.2lf\n",ans);
	}
	return 0;
}

C. BZOJ 1232: [Usaco2008Nov]安慰奶牛cheer

題目

BZOJ 1232

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct rec
{
	int x,y,z;
}edge[maxn<<1];
int fa[maxn],c[maxn],ans=0x3f3f3f3f,tot;
inline int get(int x)
{
	if (x==fa[x]) return x;
	return fa[x]=get(fa[x]);
}
inline bool cmp(rec a,rec b)
{
	return a.z<b.z;
}
int main()
{
	int n,p;read(n);read(p);
	for (int i=1;i<=n;++i)
	{
		read(c[i]);
		ans=min(ans,c[i]);
		fa[i]=i;
	}
	for (int i=1;i<=p;++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		z=(z<<1)+c[x]+c[y];
		edge[i].x=x,edge[i].y=y,edge[i].z=z;
	}
	sort(edge+1,edge+p+1,cmp);
	for (int i=1;i<=p;++i)
	{
		int x=get(edge[i].x);
		int y=get(edge[i].y);
		if (x!=y)
		{
			fa[x]=y;
			++tot;
			ans+=edge[i].z;
		}
		if (tot==n-1) break;
	}
	printf("%d\n",ans);
	return 0;
}

D. UVA1151 Buy or Build

題目

POJ 2784
LUOGU UVA1151

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1e3+5;
const int maxm=5e5+5e2;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct Node
{
	int x,y;
}node[maxn];
struct rec
{
	int x,y,z;
	bool operator < (const rec &a) const
	{
		return z<a.z;
	}
}edge[maxm];
int fa[maxn],cost[10];
int n,q,num;
vector<int>g[10];
inline int get(int x)
{
	if (fa[x]==x) return x;
	return fa[x]=get(fa[x]);
}
inline int combine(int x,int y)
{
	int xx=get(x),yy=get(y);
	if (xx==yy) return false;
	fa[xx]=yy;
	return true;
}
int Kruskal()
{
    int ans=0,cnt=0;
    for (int i=0;i<num;++i)
    {
        if (combine(edge[i].x,edge[i].y))
        {
            ans+=edge[i].z;
            ++cnt;
        }
        if (cnt==n-1) break;
    }
    return ans;
}
int main()
{
//	int t;read(t);
//	while (t--)
//	{
		read(n);read(q);
		for (int i=0;i<q;++i)
		{
			g[i].clear();int number;
			read(number);read(cost[i]);
			for (int j=0;j<number;++j)
			{
				int x;read(x);
				g[i].push_back(x);
			}
		}
		for (int i=1;i<=n;++i)
		{
			read(node[i].x);
			read(node[i].y);
		}
		for (int i=1;i<=n;++i)
			for (int j=i+1;j<=n;++j)
				edge[num].x=i,edge[num].y=j,
				edge[num++].z=(node[i].x-node[j].x)*(node[i].x-node[j].x)+(node[i].y-node[j].y)*(node[i].y-node[j].y);
		sort(edge,edge+num);
		for (int i=0;i<=n;++i)
			fa[i]=i;
    	int ans=Kruskal();
    	for (int i=0;i<(1<<q);++i)
    	{
			for (int k=0;k<=n;++k)
				fa[k]=k;
			int all=0;
			for (int j=0;j<q;++j)
			{
				if (!((i>>j)&1)) continue;//if (1!=(i>>j))
				all+=cost[j];
				for (int k=1;k<g[j].size();++k)
					combine(g[j][k],g[j][0]);
			}
			ans=min(ans,all+Kruskal());
		}
		printf("%d\n",ans);
//		if (t) puts("");
//	}
	return 0;
}

E. POJ 3522 Slim Span

題目

POJ 3522

題意

給出一個圖,若圖連通,則求最大邊與最小邊差值最小的生成樹,輸出最小差值。否則輸出-1。

題解

其實就是求邊權差值最小的生成樹。
1.首先我們看一下資料範圍,2n1000mn(n1)/22 ≤ n ≤ 100 , 0 ≤ m ≤ n(n − 1)/2,很小,所以我們就可以直接列舉最小邊。
2.接著跑最小生成樹,就是瓶頸生成樹,因為最小生成樹有一個很重要的性質:在構造生成樹時有可能選擇不同的邊,但最小生成樹的權是唯一的!所以在用kruskal演算法時第一次加入的必然是最小生成樹的最小邊權值,最小邊確定後,==最小生成樹的最大邊的權值是所以生成樹中最小的,==即為瓶頸生成樹。
3.然後更新答案即可。

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=2e5+10;
char buf[1<<15],*fs,*ft;
inline char getc(){return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++;}
inline int read()
{
	int num=0,f=1;char ch=getchar();
	while (!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while (isdigit(ch)) num=(num<<3)+(num<<1)+(ch^48), ch=getchar();
	return num*f;
}
struct rec
{
	int x,y,z;
}edge[maxn];
int fa[maxn],ans;
inline bool operator < (rec a,rec b)
{
	return a.z<b.z;
}
inline int get(int x)
{
	if (x==fa[x]) return x;
	return fa[x]=get(fa[x]);
}
int main()
{
	while (1)
	{
		int n=read(),m=read(),i=0;
		if (!n && !m) break;
		if	(n==1)
		{
			puts("0");
			exit(0);
		}
		for (i=1;i<=m;++i)
			edge[i].x=read(),edge[i].y=read(),edge[i].z=read();
		sort(edge+1,edge+m+1);
		ans=edge[m].z;//差值初始化
		for (int k=1;k<=m;++k)//以k為最小邊進行列舉
		{
			int sum=0;//最小生成樹的邊數
			for (i=1;i<=n;++i)//生成樹初始化
				fa[i]=i;
			for (i=k;i<=m;++i)//求最小生成樹
			{
				int x=get(edge[i].x);
				int y=get(edge[i].y);
				if (x==y) continue;
				fa[x]=y;
				++sum;
				if (sum==n-1)//已構成一個最小生成樹(從定義出發)
				{
					ans=min(ans,edge[i].z-edge[k].z);//最大邊減去最小邊
					break;
				}
			}
			if (i==m+1)	break;
		}
		if (ans==edge[m].z)//ans未經過改變,說明此圖未連通
			puts("-1");
		else
			printf("%d\n",ans);
	}
	return 0;
}

F. UVA10816 Travel in Desert

題目

LUOGU UVA10816

題解

首先,這是一道 生成樹最短路 的題。

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1e4+5;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct MST
{
	int fr,to;
	double tem,len;
	friend bool operator < (const MST &a,const MST &b)
	{
        return a.tem<b.tem;
    }
}tree[maxn<<1];
int ver[maxn<<1],Next[maxn<<1],head[maxn],len,num;
double edge[maxn<<1];
inline void add(int x,int y,double z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
inline void insert(int x,int y,double tem,double dis)
{
	tree[++num].fr=x,tree[num].to=y,tree[num].len=dis,tree[num].tem=tem;
}
int fa[maxn],n,m,s,t;
inline int get(int x)
{
	return fa[x]==x?x:fa[x]=get(fa[x]);
}
struct HeapNode
{
    int u;//點
    double d;//距離
    friend bool operator < (const HeapNode &a,const HeapNode &b)
	{
        return a.d>b.d;
    }
};
priority_queue<HeapNode> q;
vector<int> path;
double maxtem,dist[maxn];
bool vis[maxn];
inline void heap_dijkstra()
{
	for (register int i=1;i<=n;++i)
		dist[i]=0x3f3f3f3f,vis[i]=0,fa[i]=0;
	dist[s]=0;
	q.push((HeapNode){s,dist[s]});
	while (!q.empty())
	{
		int x=q.top().u;
		q.pop();
		if (vis[x]) continue;
		vis[x]=1;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i];
			double z=edge[i];
			if (dist[y]>dist[x]+z)
			{
				dist[y]=dist[x]+z;
				fa[y]=x;//再跑最短路的同時,用並查集確定x與y之間的關係,即x是y的父親
				q.push((HeapNode){y,dist[y]});
			}
		}
	}
	register int x=t;
    path.clear();
    while (x!=s)//從終點出發,一直在樹上遍歷到起點,就剛好把路徑加入到path[]中
	{
        path.push_back(x);
        x=fa[x];
    }
    path.push_back(s);
    for (register int i=path.size()-1;i>=1;--i)
        printf("%d ",path[i]);
    printf("%d\n",path[0]);
}
int main()
{
	while (~scanf("%d%d",&n,&m))
	{
		num=len=0;maxtem=0;
		memset(head,0,sizeof(head));
		memset(fa,0,sizeof(fa));
		read(s);read(t);
		register double te,w;
		for (register int i=1,fr,to;i<=m;++i)
		{
			read(fr);read(to);
			scanf("%lf%lf",&te,&w);
			insert(fr,to,te,w);
		}
		for (register int i=1;i<=n;++i)
			fa[i]=i;
		sort(tree+1,tree+m+1);
		for (register int i=1;i<=m;++i)
		{
			register int x=get(tree[i].fr),y=get(tree[i].to);
			if (x!=y)
			{
				fa[x]=y;
				maxtem=max(maxtem,tree[i].tem);
				if (get(s)==get(t)) break;
			}
		}
		for (int i=1;i<=m;++i)
		{
			if (tree[i].tem>maxtem) break;;
            add(tree[i].fr,tree[i].to,tree[i].len);
            add(tree[i].to,tree[i].fr,tree[i].len);
		}
		heap_dijkstra();
		printf("%.1f %.1f\n",dist[t],maxtem);
	}
	return 0;
}

G. POJ 1639 Picnic Planning 最小度數限制生成樹

題目

POJ 1639
LUOGU UVA1537

題解

假設Park的停車數沒有限制,那麼這題就是一道最小生成樹了。
但是本題限制Park的停車數不能超過k,把Park看做根節點記為V0,那麼就是說它的度數不能超過k。

得到一棵k度限制生成樹的步驟:

  1. 忽略根節點做一次kruskal,此時得到的是一個森林,包含了m個最小生成樹。

  2. 對於每一顆最小生成樹,選擇其中離根節點最近的點,向根節點連一條邊,此時得到了一棵m度的最小生成樹。

  3. 由m度生成樹得到m+1度生成樹:

    (1). 用dp預處理出當前生成樹中從V0到點i的路徑上與V0無關聯的權值最大的邊,記為dp[i].zdp[i].z,該邊的兩端點記為dp[i].xdp[i].xdp[i].ydp[i].y。(及程式碼中的dfs函式預處理)

    (2). 對於一個不在生成樹中的邊&lt;V0v&gt;&lt;V_{0},v&gt;, 如果將該邊加入生成樹中,則一定會得到一個環。
    此時我們刪掉環中權值最大的邊,即(1)中預處理得到的dp[v]dp[v],得到一棵m+1度的生成樹。

    (3). 對於(2)列舉每一個v,記minnum=minV0,vdist[v].zminnum=min((V_{0}, v) - dist[v].z ),使minnum得到最小值的點vv就是這次選擇的點。連線V0vV_{0},v,刪去dp[v]dp[v]

  4. 重複步驟3直到得到一棵k度限制生成樹。本題要求度數不超過k,所以在某一步中,minnum>=0,就可以輸出答案了。

關於minnumminnum的含義:

minnumminnum為在從m度生成樹得到m+1度生成樹的過程中,選擇一個點v(連線V0vV_{0},v,刪去dp[v]dp[v])可以得到的最大利益,即生成樹的值最多可以減少多少

minnumminnum為負數,表示選擇點v可以生成樹的值減少,那麼使minnum最小的點就是可以使生成樹的值減少最多的點,這次我們便選擇它。

如果minnumminnum>=0,說明得到m+1度生成樹不會獲得任何利益,就不用繼續下去,直接輸出答案即可。

程式碼

友情提示:想在洛谷上過這道題,我註釋掉的語句就必須加上,少一句就可能是WA或TLE。 ̄ω ̄=

/*****************************************
	Memory: 156K		Time: 16MS
	Language: C++		Result: Accepted
*****************************************/
#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=35,inf=0x3f3f3f3f;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct mst
{
	int x,y,z,next;
	inline bool operator < (const mst a) const
	{
		return z < a.z;
	}
}f[maxn<<1];
int k,tot,ans,fa[maxn],a[maxn][maxn],d[maxn],v[maxn];
map<string,int>m;
vector<mst>e;
bool b[maxn][maxn];
inline int get(int x)
{
	if (fa[x]==x) return x;
	return fa[x]=get(fa[x]);
}
inline void dfs(int x,int o)
{
	for (int i=2;i<=tot;++i)
	{
		if (i==o || !b[x][i]) continue;
		if (f[i].z==-1)
		{
			if (f[x].z>a[x][i]) f[i]=f[x];
			else
				f[i].x=x,f[i].y=i,f[i].z=a[x][i];
		}
		dfs(i,x);
	}
}
int main()
{
//	int t;read(t);
//	while (t--)
//	{
		int n;read(n);
//		ans=0;
		memset(a,0x3f,sizeof(a));
		memset(d,0x3f,sizeof(d));
//		memset(f,0,sizeof(f));
//		memset(v,0,sizeof(v));
//		memset(b,0,sizeof(b));
//		m.clear(),e.clear();
		m["Park"]=tot=1;
		for (int i=0;i<maxn;++i)
			fa[i]=i;
		for (int i=1;i<=n;++i)
		{
			mst w;
			string s1,s2;
			cin>>s1>>s2;read(w.z);
			w.x=m[s1]?m[s1]:(m[s1]=++tot);
			w.y=m[s2]?m[s2]:(m[s2]=++tot);
			e.push_back(w);
			a[w.x][w.y]=a[w.y][w.x]=min(a[w.x][w.y],w.z);
		}
		read(k);
		sort(e.begin(),e.end());
		for (unsigned int i=0;i<e.size();++i)
		{
			if (e[i].x==1 || e[i].y==1) continue;
			int x=get(e[i].x),y=get(e[i].y);
			if (x!=y)
			{
				fa[y]=x;
				b[e[i].x][e[i].y]=b[e[i].y][e[i].x]=1;
				ans+=e[i].z;
			}
		}
		for (int i=2;i<=tot;++i)
			if (a[1][i]!=inf)
			{
				int root=get(i);
				if (d[root]>a[1][i])
					d[root]=a[1][v[root]=i];
			}
		for (int i=1;i<=tot;++i)
			if (d[i]!=inf)
			{
				--k;
				b[1][v[i]]=b[v[i]][1]=1;
				ans+=a[1][v[i]];
			}
		while (k--)
		{
			memset(f,-1,sizeof(f));
			f[1].z=-inf;
			for (int i=1;i<=tot;++i)
				if (b[1][i])
					f[i].z=-inf;//QWQ
			dfs(1,-1);
			int o,w=-inf;//QAQ
			for (int i=2;i<=tot;++i)
				if (w<f[i].z-a[1][i])
					w=f[i].z-a[1][o=i];
			if (w<=0) break;
			b[1][o]=b[o][1]=1;
			b[f[o].x][f[o].y]=b[f[o].y][f[o].x]=0;
			ans-=w;
		}
		printf("Total miles driven: %d\n",ans);
//		if (t) puts("");
//	}
	return 0;
}

相關文章