Week10-图论-最小生成树、并查集
第一题
评析:这道题就是一个简单的欧拉回路的模板,统计每个点的度数,如果每个点的度数都为偶数,那么就可以一笔画(因为每个点都有进有出),否则统计度数为奇数的点的个数(记为num)答案就是num/2(每次都把度数为奇数的点分别作为起点和终点)。
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int n,m,ans,x,y;
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>x>>y;
a[x]++;
a[y]++;
}
for(int i=1;i<=n;i++)
{
if(a[i] % 2 == 1) ans++;
}
if(ans)
cout<<(ans/2);
else
cout<<(ans+1);
return 0;
}
第二题
评析:并查集的应用
#include<bits/stdc++.h>
using namespace std;
int pre[1000000];
int find(int x)
{
if (x != pre[x])
return find(pre[x]);
else
return x;
//如果集合i的父亲是自己,说明自己就是源头,返回自己的标号,否则查找集合i的父亲的源头
}
int main()
{
int m,n,num,k;
cin>>m>>n>>k;
num=m*n;
for (int i = 1; i <= num; i++)
pre[i] = i;
while (k--)
{
int a,b;
cin>>a>>b;
int ra, rb;
ra=find(a);
rb=find(b);
if (ra != rb)//看两个集合是不是同一源头
pre[ra]=rb;
}
int sum=0;
for (int i = 1; i <= num; i++)
if (i == pre[i]) sum++;
cout<<sum;
return 0;
}
第三题
评析:注意如果两球的半径之和大于等于两个球球心的距离,那么两圆相交或相切,具体方法见注释
#include <bits/stdc++.h>
using namespace std;
struct pos
{
double x, y, z;
} p[1005];
long long t, n, r, h;
bool a[1005], f;
// 求两点是否相邻
inline bool dis(pos a, pos b)
{
long long s = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) +(a.z - b.z) * (a.z-b.z);
return s <= 4 * r * r;
}
void F(int x)
{
// 标记为true(上表面),退出去
if (f == true) return ;
// 上表面,标记为true
if (p[x].z + r >= h)
{
f = true;
return ;
}
for (int i = 1; i <= n; ++i)
{
// 跑过这个点就跳过(记忆化)
if (a[i] == true) continue;
// 没跑过且相邻,跑!
if (dis(p[x], p[i]))
{
a[i] = true;
F(i);
}
}
}
int main()
{
cin >> t;
while (t--)
{
// 不要忘记重置标记哦
f = 0;
cin >> n >> h >> r;
for (int i = 1; i <= n; ++i) {
cin >> p[i].x >> p[i].y >> p[i].z;
a[i] = false;
}
for (int i = 1; i <= n; ++i)
{
// 下表面,起跑
if (p[i].z <= r)
{
a[i] = true;
F(i);
}
}
if (f == true) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
第四题
评析:这一题是对Floyd算法的理解,终于还是自己搞定了,这里与模板不同之处也就是多了一个修路事件,只要判断这一个条件之后就和原来一样了。
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,z;
int a[205];
int f[205][205];
inline void updata(int k){//更新方式
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(f[i][j]>f[i][k]+f[j][k]) f[i][j]=f[j][i]=f[i][k]+f[j][k];//Floyd板子
return;
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if (i==j) f[i][j]=0;//和自己那就距离为0
else f[i][j]=1e9;//其余初始化为无穷大
}
for(int i=1;i<=m;i++)
{
cin>>x>>y>>z;
f[x][y]=f[y][x]=z;//邻接表存图
}
int q;
cin>>q;
int now=0;
for(int i=1;i<=q;i++)
{
cin>>x>>y>>z;
while(a[now]<=z && now<n)//更新条件
{
updata(now);
now++;
}
if(a[x]>z || a[y]>z) cout<<"-1"<<endl;//无法成功
else
{
if(f[x][y]==1e9)cout<<"-1"<<endl;//无法成功
else cout<<f[x][y]<<endl;
}
}
return 0;
}
第五题
评析:记录最小生成树的最大边 然后判断每只猴子的跳跃距离是否大于最大边的权值
如果满足条件即ans++
#include<bits/stdc++.h>
using namespace std;
int m,n,h[505],f[1005],cnt,px,py,ans;
double flag;
struct p1
{
int x,y;
}a[1005];
struct p2
{
int x,y;
double z;
}b[1000005];
bool cmp(p2 a,p2 b){
return a.z<b.z;
}
int find(int x){
if (x==f[x]) return x;
return f[x]=find(f[x]);
}
double dis(int x1,int y1,int x2,int y2)
{
return sqrt(double(x1-x2)*(x1-x2)+double(y1-y2)*(y1-y2));
}
int main()
{
cin>>m;
for (int i=1;i<=m;i++) cin>>h[i];
cin>>n;
for (int i=1;i<=n;i++) f[i]=i;
for (int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
{
cnt++;
b[cnt].x=i;
b[cnt].y=j;
b[cnt].z=dis(a[i].x,a[i].y,a[j].x,a[j].y);
}
sort(b+1,b+cnt+1,cmp);
for (int i=1;i<=cnt;i++)
{
px=find(b[i].x);
py=find(b[i].y);
if (px==py) continue;
f[px]=py;
flag=b[i].z;
}
for (int i=1;i<=m;i++)
if (h[i]*1.0>=flag) ans++;
cout<<ans;
return 0;
}