牛客算法竞赛入门课第四节习题 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;
} 
京公网安备 11010502036488号