Week 10

        图的练习

1.P1636 Einstein学画画 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

前置:

        欧拉路(七桥问题):是否可从某个地方出发,经过每座桥一次,回到原来出发的地方?

相关图概念:

           顶点的度,就是指和该顶点相关联的边数。

           出度:有向图中从某顶点出发的边数。

           入度:有向图中在某顶点结束的边数。

欧拉回路:

        若恰通过图中每条边一次回到起点,则称该回路为欧拉(Euler)回路。具有欧拉回路的图称为欧拉图。
定理1:
        一个无向图是欧拉图,当且仅当该图所有顶点度数都是偶数。
        一个有向图是欧拉图,当且仅当该图所有顶点度数都是0(入度与出度之和)。
定理2:
        存在欧拉回路的条件:图是连通的,且不存在奇点(顶点度数为奇数)

欧拉路:

        若从起点到终点的路径恰通过图中每条边一次(起点与终点是不同的点),则该路径称为欧拉路。
定理1:
        存在欧拉路的条件:图是连通的,且存在2个奇点。

        如果存在2个奇点,则欧拉路一定是从一个奇点出发,以另一个奇点结束。

定理:一个连通图只可能有偶数个奇点。

解析:

        通过欧拉路我们可以知道如果此题提供的图有两个奇点,那么我们便可以一笔画完。如果有多个奇点,我们可以将该图视为多个两奇点的图叠加而成,有多少个两奇点,便要画几次。所以我们统计图中奇点的个数,除二即可。

        另外,如果图中没有奇点,那么该图是欧拉回路,可以直接一笔画成,需要特判。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
int du[200010];
int cnt;
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		du[a]++;du[b]++;
	}
	for(int i=1;i<=n;++i)
	{
		if(du[i]%2) ++cnt;//统计奇点
	}
	int ans=0;
	if(!cnt) ans=1;//欧拉回路
	else ans=cnt/2;
	printf("%d",ans);
	return 0;
 } 

2.P8654 [蓝桥杯 2017 国 C] 合根植物 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

前置:并查集

         并查集是一种可以动态维护若干个不重叠的集合,并支持合并与查找的数据结构。每一个集合都找出一个代表,该集合中的其他点都最终指向该代表。

解析:

        题目中将多格植物并为一株,即用并查集将多格植物编号并为同一集合,每个集合有一个代表编号(初始代表编号为自身编号),处于不同编号下的就是不同集合,不同植株。

        由于除了代表的编号还是自身,其他格子的编号都指向代表,所以最终统计有多少个编号指向自身的就有多少个集合。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
int fa[1000010];
int vis[1000010];
int sf(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=sf(fa[x]);
}
void bing(int r1,int r2)
{
	fa[r2]=r1;
}
int main()
{
	int n,m;scanf("%d%d",&m,&n);
	int k;scanf("%d",&k);
	for(int i=1;i<=n*m;++i) fa[i]=i;
	for(int i=1;i<=k;++i)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		int r1=sf(a),r2=sf(b);//查找各自的代表编号
		if(r1!=r2) bing(r1,r2);//将二者并入同一集合
	}
	int ans=0;
	
	for(int i=1;i<=m*n;++i)
	{
		if(fa[i]==i) ++ans;
	}
	printf("%d",ans);
	return 0;
 } 

3.P3958 [NOIP2017 提高组] 奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解析:

        同样用到并查集。先用并查集将相通的洞并入同一集合,如果与上下底面都相通的洞处于同一集合,那么就可以利用已有的空洞跑到最上面。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct node
{
	int x,y,z;
}d[1010];
int di[1010],ding[1010];
int cnt1,cnt2;
int fa[1010];
int sf(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=sf(fa[x]);
}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		cnt1=cnt2=0;
		int n,h,r;scanf("%d%d%d",&n,&h,&r);
		for(int i=1;i<=n;++i) fa[i]=i;
		for(int i=1;i<=n;++i)
		{
			scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].z);
			if(d[i].z-r<=0) di[++cnt1]=i;//与下底面接触
			if(d[i].z+r>=h) ding[++cnt2]=i; //与上底面接触
		}
		for(int i=1;i<n;++i)
		{
			for(int j=i+1;j<=n;++j)
			{
				double k=pow(d[i].x-d[j].x,2)+pow(d[i].y-d[j].y,2)+pow(d[i].z-d[j].z,2);
				if(sqrt(k)<=2*r)//两洞相通,并入同一集合
				{
					int r1=sf(i),r2=sf(j);
					if(r1!=r2) fa[r2]=r1;
				}
			}
		}
		int ans=0;
		for(int i=1;i<=cnt1;++i)
		{
			for(int j=1;j<=cnt2;++j)
			{
                //同一集合且与上下都相通
				if(sf(di[i])==sf(ding[j])) ans=1;
			}
		}
		if(ans==1) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
 } 

4.P1119 灾后重建 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解析:

        floyd的深层次运用。学习floyd时,三层循环表示直接从i到j与经过k点的从i到j相比,距离是否缩短。回到此题,初始从i村到j村间的距离已知,现在k村庄修好,我们需要判断dis[i][j]与dis[i][k]+dis[k][j]的大小,正契合floyd的思想。于是我们将三层循环中的最外层加上时间这一判断条件即可。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int t[210];
int f[210][210];
int n,m;
void floyd(int k)
{
	for(int i=0;i<n;++i)
	for(int j=0;j<n;++j)
		if(f[i][j]>f[i][k]+f[k][j])
			f[i][j]=f[j][i]=f[i][k]+f[k][j];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;++i)
		for(int j=0;j<n;++j)
			f[i][j]=100000;
	for(int i=0;i<n;++i)
	{
		scanf("%d",&t[i]);f[i][i]=0;
	}
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		f[x][y]=f[y][x]=z;
	}
	int q;scanf("%d",&q);
	int tot=0,cnt=0;
	for(int i=1;i<=q;++i)//由于按时间从小到大,直接遍问边答
	{
		int a,b,ti;
		scanf("%d%d%d",&a,&b,&ti);
		while(t[tot]<=ti&&tot<n)//时间允许
		{
			floyd(tot);++tot;
		}
		int ans;
		if(t[a]>ti||t[b]>ti) ans=-1;
		else if(f[a][b]==100000) ans=-1;
		else ans=f[a][b];
		printf("%d\n",ans);
	}
	return 0;
}

5.P2504 [HAOI2006]聪明的猴子 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

解析:

        借助并查集实现的krukal算法(最小生成树)。在生成是判断长边是否可跳过。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node
{
	int x,y;
	double val;
}edg[100100];
int fa[10100];
int m,n;
int a[10010],b[10010];
int d[5100];
int cnt=0;
bool cmp(node a,node b)
{
	return a.val<b.val;
}
int find(int x)
{
	if(fa[x]==x) return x;
	else return fa[x]=find(fa[x]);
}
int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;++i) scanf("%d",&d[i]);
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&a[i],&b[i]);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			if(i==j) continue;
			++cnt;
			edg[cnt].x=i;edg[cnt].y=j;
			double val=pow(a[i]-a[j],2)+pow(b[i]-b[j],2);
			edg[cnt].val=sqrt(val);
		}
	}
	for(int i=1;i<=n;++i) fa[i]=i;
	sort(edg+1,edg+cnt+1,cmp);
	int k=0;
	double maxx=0;
	for(int i=1;i<=cnt;++i)
	{
		int x=find(edg[i].x),y=find(edg[i].y);
		if(x==y) continue;
		fa[y]=x;
		maxx=edg[i].val;
		++k;if(k==n-1) break;
	}
	int ans=0;
	for(int i=1;i<=m;++i)
		if(d[i]>=maxx) ++ans;
	printf("%d",ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值