前言:本周学了最小生成树的生成和并查集。最小生成树的生成方法为prime算法,做题时也了解到了Kruskal算法。
一、欧拉图
题目:洛谷P1636
思路:无需建图,只需用到欧拉图的知识,判断奇点的个数。(参考了题解第一个)
/*本题考察的是欧拉回路*/
#include<bits/stdc++.h>
using namespace std;
int num[1010],n,m,sum;//num表示的是图中每个点的度数,度数就是指与这个点关联的边的条数.sum指的是奇点的总数。
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
num[x]++;
num[y]++;
}
for(int i=1;i<=n;i++)
{
if(num[i]&1==1)//判断这个点是否为奇点,奇点指的是这个点的度数为奇数
{
sum++;
}
}
if(sum==0)printf("%d",1);
else printf("%d",sum/2);
system("pause");
return 0;
}
二、并查集
题目:并查集
思路:在合并后,遍历整个数组,判断父节点是自身的植物的数量,就是答案
#include<bits/stdc++.h>
using namespace std;
const int MAX=10010;
long long wuqiong=0x3f3f3f3f;
struct edge
{
int end , val;//end表示终点,val表示该条边的权重。
};
vector <edge> node[MAX];//node[i]表示从i出发到end建立无向边,权重为val。
int m,n;
int sum[MAX][MAX];
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
node[x].push_back({y,z});
node[y].push_back({x,z});
}
for(int i=1;i<=n;i++)
{
sum[i][i]=0;
for(int j=0;j<node[i].size();j++)
{
sum[i][node[i][j].end]=node[i][j].val;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j||sum[i][j])continue;
else sum[i][j]=wuqiong;
}
}
for(int k=1;k<=n;++k)
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
sum[i][j]=min(sum[i][j],sum[i][k]+sum[k][j]);
}
}
}
for(int i=1;i<=n;++i)
{
int num=0;
for(int j=1;j<=n;++j)
{
num+=sum[i][j];
}
cout<<num<<endl;
}
system("pause");
return 0;
}*/
//这是之前邻接表建图做的,不知道哪里有问题,下面是之际直接用邻接矩阵存值做的,套的floyd模板。
#include <bits/stdc++.h>
using namespace std;
/*负无穷大为0xc0c0c0c0*/long long inf=0x3f3f3f3f,d[5001][5001];//d[i][j]表示从i到j的最短路径,分三种情况:1.到本身的距离为0 2.若i和j相连,则距离已知 3.若两点未连接,则距离为无穷大。
const long long mod=998244354;
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j]=inf;
d[i][i]=0;
}
}
for(int i=1;i<=m;i++){
long long x,y,l;
cin >> x >> y >> l;
d[x][y]=min(l,d[x][y]);
d[y][x]=min(l,d[y][x]);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);//floyd模板
for(int i=1;i<=n;i++){
int sum=0;
for(int j=1;j<=n;j++)
if(d[i][j] != inf)sum=(sum+d[i][j])%mod;
cout << sum << endl;
}
system("pause");
return 0;
}
题目:洛谷P3958
思路:题解中有很多用的搜索算法,这里还是为了加深并查集的应用,用并查集来做。只需判断这个集合是否能够从下表面到达上表面
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int r1,r2,t,n,father[1002];
long long r,h,x[1002],y[1002],z[1002];
int find(int u)
{
if(father[u]!=u)
return find(father[u]);
return father[u];
}
void unionn(int u,int v)
{
r1=find(u);
r2=find(v);
father[r2]=r1;
}
void ok(int u)
{
if(z[u]+r>=h)
unionn(n+1,u);
if(z[u]-r<=0)
unionn(0,u);
}
long long dis(int u,int v)
{
return sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v])+(z[u]-z[v])*(z[u]-z[v]));
}
bool pd(int u,int v)
{
if(dis(u,v)<=2*r)
return true;
else
return false;
}
bool same(int u,int v)
{
if(find(u)==find(v))
return true;
else
return false;
}
int main()
{
scanf("%d",&t);
for(int l=1;l<=t;l++)
{
scanf("%d%lld%lld",&n,&h,&r);
for(int i=1;i<=n;i++)
scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
for(int i=0;i<=n+1;i++)
father[i]=i;
for(int i=1;i<=n;i++)
ok(i);
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++)
if(pd(i,j))
unionn(i,j);
if(same(0,n+1))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
三、最小生成树
题目:洛谷P2504
思路:用Kruskal算法来建立最小生成树,将最大的边的权重和每个猴子的最远跳跃距离相比较即可判断。
#include<bits/stdc++.h>
using namespace std;
struct Edge
{
int u,v;
double val;
}e[2000010];
int m,n,cnt,ans,num;
double maxans;
double a[510];
int x[1010],y[1010],fa[1010];
int find(int x)
{
if (fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void unnion(int x,int y)
{
x=find(x);y=find(y);
if (x!=y) fa[x]=y;
}
bool judge(int x,int y)
{
x=find(x);y=find(y);
if (x==y) return 1;
else return 0;
}
void add(int u,int v,double w)
{
e[++cnt].u=u;e[cnt].v=v;e[cnt].val=w;
}
void kruskal()
{
int u,v;
for(int i=1;i<=cnt;i++)
{
u=e[i].u;v=e[i].v;
if (!judge(u,v)) maxans=e[i].val,num++,unnion(u,v);
if (num>n-1) break;
}
}
bool cmp(Edge a,Edge b)
{
return a.val<b.val;
}
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%lf",&a[i]);
scanf("%d",&n);for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if (i!=j) add(i,j,sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
sort(e,e+cnt+1,cmp);kruskal();
for(int i=1;i<=n;i++) if (a[i]>=maxans) ans++;
printf("%d\n",ans);
return 0;
}
四、Floyd算法加深理解
题目:洛谷P1119
#include<iostream>
#include<cstdio>
#include<cstring>
#define ss 303
using namespace std;
inline int read(){
int x(0),f(1);char c(getchar());
for(;!isdigit(c);c=getchar()) c=='-'?f=-1:1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
int f[ss][ss],n,m,tim[ss];
bool b[ss];
inline void adaa(int k){
if(b[k]) return ;b[k]=1;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
main(){
n=read();m=read();
for(register int i=0;i<n;i++) tim[i]=read();
memset(f,0x3f,sizeof(f));
for(register int i=1;i<=m;i++){
int x(read()),y(read()),z(read());
f[x][y]=f[y][x]=z;
}
int Q(read());
while(Q--){
int x(read()),y(read()),ti(read());
int k;
for(k=n-1;k>=0;k--) if(tim[k]<=ti) break;
for(register int i=0;i<=k;i++) adaa(i);
if(f[x][y]==1061109567||x>k||y>k){
printf("-1\n");continue;
}
printf("%d\n",f[x][y]);
}
}