牛客算法竞赛入门课第四节习题 Part1(并查集基础)
加边的无向图 (思维+并查集)
题意:
给你一个 n 个点,m 条边的无向图,求至少要在这个的基础上加多少条无向边使得任意两个点可达
思路:
给定的m条边不一定能够联通所有点,只需要计算出联通块个数-1即可。
因为3个区域只需要2条边就可以联通,所以答案是个数-1。
求联通块的话,可以用并查集或dfs
类似的题:传送门
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; ///联通块个数-1 int root[maxn],n,m; int Find(int x){ if(x!=root[x]) root[x]=Find(root[x]); return root[x]; } void Union(int x,int y){ x=Find(x),y=Find(y); if(x!=y) root[y]=x; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) root[i]=i;///并查集初始化 int a,b; while(m--){ scanf("%d%d",&a,&b); Union(a,b); } int num=0; for(int i=1;i<=n;i++) if(root[i]==i) num++; printf("%d\n",num-1); return 0; }
任意点(特殊联通+并查集)
题意:
平面上有若干个点,从每个点出发,你可以往东南西北任意方向走,直到碰到另一个点,然后才可以改变方向。 请问至少需要加多少个点,使得点对之间互相可以到达。
思路:
相当于是加边的无权图的加强版。不同的是两个点的联通条件是横坐标或纵坐标相同。
代码:
#pragma GCC optimize(2) #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int>PII; #define I_int ll inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } char F[200]; inline void out(I_int x) { if (x == 0) return (void) (putchar('0')); I_int tmp = x > 0 ? x : -x; if (x < 0) putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0) putchar(F[--cnt]); cout<<endl; } const int maxn=1100; PII a[maxn]; int root[maxn]; int Find(int x){ if(x!=root[x]) root[x]=Find(root[x]); return root[x]; } void Union(int x,int y){ x=Find(x),y=Find(y); if(x!=y) root[x]=y; } int main(){ int n=read(); for(int i=1;i<=n;i++) root[i]=i;///并查集初始化 for(int i=1;i<=n;i++){ a[i].first=read(); a[i].second=read(); } for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) if(a[i].first==a[j].first||a[i].second==a[j].second) Union(i,j); int res=0; for(int i=1;i<=n;i++) if(root[i]==i) res++; out(res-1); return 0; }
B-经商 (并查集+01背包)
题意:
给定关系网和每个人的消耗精力值和利益值,问在能力范围内最多能够获得的利益值是多少。
思路:
简单但又综合的题目。
首先因为他只能和能接触到的人交际,所以首先要根据并查集判断出有哪些人和他交际,即在一个联通块内。
然后就是一个背包问题了,跑一遍01背包即可。
#pragma GCC optimize(2) #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int>PII; #define I_int ll inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } char F[200]; inline void out(I_int x) { if (x == 0) return (void) (putchar('0')); I_int tmp = x > 0 ? x : -x; if (x < 0) putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0) putchar(F[--cnt]); cout<<endl; } const int maxn=100010; int root[maxn]; int n,m,c; int a[maxn],b[maxn]; int v[maxn],w[maxn]; int dp[maxn]; int Find(int x){ if(x!=root[x]) root[x]=Find(root[x]); return root[x]; } void Union(int x,int y){ x=Find(x),y=Find(y); if(x!=y) root[x]=y; } int main(){ int T=read(); while(T--){ n=read();m=read();c=read(); for(int i=1;i<=n;i++) root[i]=i; for(int i=2;i<=n;i++) a[i]=read(),b[i]=read(); while(m--){ int x=read(),y=read(); Union(x,y); } int Root=Find(1),tot=0; for(int i=2;i<=n;i++) if(Root==Find(i)) w[tot]=a[i],v[tot]=b[i],tot++; memset(dp,0,sizeof dp); for(int i=0;i<tot;i++) for(int j=c;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); cout<<dp[c]<<endl; } return 0; }