NEUQ-ACM 并查集+二分查找

P3367 【模板】并查集

本题算是最基础的并查集问题了,通过find函数通过递归+路径压缩维护并查集即可。

#include<iostream>
using namespace std;
int aa[10009];
int Find(int a)
{
	if (aa[a] == a)return a;
	return aa[a] = Find(aa[a]);
}
int main()
{
	int n, m, a, b, c;
	cin >> n >> m;
	for(int i=1;i<=n;i++)
		aa[i]=i;
	for (int i = 1; i <= m; i++)
	{
		cin >> a >> b >> c;
		if(a==1)
			aa[Find(b)] = Find(c);
		else
			if (Find(b) == Find(c))
				cout << 'Y' << endl;
			else
				cout << 'N' << endl;
	}
	return 0;
}

P8604 [蓝桥杯 2013 国 C] 危险系数

本题要求关键点的个数,本人的做法是通过dfs找出所有的路径,每找到一条路径,就把路径上的点都标记一下,最后通过判断点被标记的次数和总路径数作比较即可找出所有的关键点,注意最终答案需要把起点这个点减去。

#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 1009;
int n;
int g[MAXN][MAXN];
int u, v;
long long int num[MAXN];
long long int road;
int vis[MAXN];
void dfs(int a)
{
	if (a == v)
	{
		for (int i = 1; i <= n; i++)
			if (vis[i] == 1)num[i]++;
		road++;
		return;
	}
	vis[a] = 1;	
	for (int i = 1; i <= n; i++)
		if (g[a][i] == 1 && vis[i] == 0&&a!=i)
			dfs(i);
	vis[a] = 0;
}
int main()
{
	int k;
	cin >> n >> k;
	while (k--)
	{
		cin >> u >> v;
		g[u][v] = 1;
		g[v][u] = 1;
	}
	cin >> u >> v;
	dfs(u);
	int ans=0;
	for (int i = 1; i <= n; i++)
	{
		if (num[i] == road)
			ans++;
	}
	ans--;
	cout << ans;
	return 0;
}

P1330 封锁阳光大学

本题乍一看没有任何思路,需要找出问题的特点,此问题中,对于一条路,两个点一定有一个没有河蟹,而另一个有河蟹。如果规定一个点有河蟹,那么其他的点是否有河蟹可根据上述规则确定出来,通过递归对点进行染色(被染色的表示有河蟹的点),并不断判断有没有不符合上述规则的点,不符合的话,该情况就不符合。递归的时候顺便几下被染色的点的总是。将所有的点都遍历完,共n个dfs,并比较染色点数即可。注意本题需要用链表记录所有的路。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
struct Edge
{
	int to;
	int nex;
}edge[200009];
int n, m;
int head[10009],vis[10009],sum[2],col[10009];
int cnt;
void add(int a, int b)
{
	cnt++;
	edge[cnt].to = b;
	edge[cnt].nex = head[a];
	head[a] = cnt;
}
bool dfs(int a,int color)
{
	if (vis[a])
	{
		if (col[a] != color)
			return false;
		return true;
	}
	vis[a] = 1;
	sum[color]++;
	col[a] = color;
	bool tf = true;
	for (int i = head[a]; tf && i; i = edge[i].nex)
	{
		tf = tf && dfs(edge[i].to, 1 - color);
	}
	return tf;
}
int main()
{
	int ans = 0;
	cin >> n >> m;
	int u, v;
	while (m--)
	{
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	for (int i = 1; i <= n; i++)
	{
		if (vis[i])continue;
		sum[0] = 0; sum[1] = 0;
		if (dfs(i, 0))
			ans += min(sum[0], sum[1]);
		else
		{
			cout << "Impossible";
			return 0;
		}
	}
	cout << ans;
	return 0;
}

P3916 图的遍历

本题N范围较大,通过链表存图。本题选择反向存图,从最大的数开始遍历,将所有经过的数标记,这些经过的数他们的最终答案一定是这个最大的数,按照这个规则,从大往小,进行dfs并进行标记即可。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
struct Edge
{
	int to;
	int nex;
}edge[100009];
int n, m,num;
int head[100009];
int cnt;
int ans[100009];
void dfs(int a)
{
	if (ans[a])
		return;
	ans[a] = num;
	for (int i = head[a]; i; i = edge[i].nex)
	{
		dfs(edge[i].to);
	}
}
void add(int a, int b)
{
	cnt++;
	edge[cnt].to = b;
	edge[cnt].nex = head[a];
	head[a] = cnt;
}
int main()
{
	cin >> n >> m;
	int u, v;
	while (m--)
	{
		cin >> u >> v;
		add(v, u);
	}
	for (num = n; num >= 1; num--)
	{
		dfs(num);
	}
	for (int i = 1; i <= n; i++)
	{
		cout << ans[i];
		if (i != n)cout << " ";
	}
	return 0;
}

P1119 灾后重建

此题本人Floyd还没学,选择用Dijkstra去做,此题对于确定的x,y,t直接套模板即可,值得注意的是,本题从t0到tn-1是不减的,而且输入的t也是不减的,因此,对于每一个t,无需重新构建图,可以利用上一个t对应的图进行修改即可,同时,Dijkstra算法中,也无需for循环到最后,一定是到一个点,之后所有点都还没有修好。通过这些细节进行时间复杂度优化。

#include<iostream>
#include<string.h>
using namespace std;
long long int Map[203][203];
long long int dist[203];
int vis[203];
int tt[203];
long long int map[203][203];
int main()
{
	long long int n, m, u, v, d;
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> tt[i];
	for (int i = 1; i <= m; i++)
	{
		cin >> u >> v >> d;
		Map[u][v] = d;
		Map[v][u] = d;
	}
	int Q;
	cin >> Q;
	while (Q--)//!0:\n
	{
		int x, y, t;
		cin >> x >> y >> t;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
			{
				map[i][j] = (tt[i] <= t && tt[j] <= t) ? Map[i][j] : 0;
				if (map[i][j] == 0)
					map[i][j] = 2147483647;
			}
		long long int min = 0;
		int minncul = x;
		int minnnex = x;
		memset(vis, 0, sizeof(vis));
		for (int i = 0; i < n; i++)
			dist[i] = 2147483647;
		dist[x] = 0;
		vis[x] = 1;
		while (min != 2147483647)
		{
			min = 2147483647;
			for (int j = 0; j < n; j++)
			{
				if (vis[j])continue;
				if (map[minncul][j] + dist[minncul] < dist[j])
					dist[j] = map[minncul][j] + dist[minncul];
				if (dist[j] < min)
				{
					min = dist[j];
					minnnex = j;
				}
			}
			vis[minnnex] = 1;
			minncul = minnnex;
			if (minncul == y)break;
		}
		if (dist[y] == 2147483647)
			cout << -1;
		else
			cout << dist[y];
		if (Q != 0)cout << endl;
	}
	return 0;
}

P1525 [NOIP2010 提高组] 关押罪犯

本题需用并查集去做,根据要求需要使市长看到的最小,因此根据贪心思想,将所有怨气值排序,从大到小进行分配即可,知道无法分配时的怨气值即为最小的。

#include<iostream>
#include<algorithm>
using namespace std;
struct Edge
{
	int x, y, z;
}edge[100005];
int n, m, pre[20005], b[20005];
bool cmp(Edge a, Edge b)
{
	return a.z > b.z;
}
int find(int x)
{
	if (pre[x] == x)return x;
	else
		return pre[x] = find(pre[x]);
}
void add(int x, int y)
{
	pre[find(pre[x])] = find(pre[y]);
}
bool check(int x, int y)
{
	if (find(x) == find(y))
		return true;
	else
		return false;
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		pre[i] = i;
	for (int i = 1; i <= m; i++)
		cin >> edge[i].x >> edge[i].y >> edge[i].z;
	sort(edge + 1, edge + 1 + m, cmp);
	for (int i = 1; i <= m; i++)
	{
		if(check(edge[i].x, edge[i].y))
		{
			cout << edge[i].z; return 0;
		}
		else
		{
			if (!b[edge[i].x])b[edge[i].x] = edge[i].y;
			else
				add(b[edge[i].x], edge[i].y);
			if (!b[edge[i].y])b[edge[i].y] = edge[i].x;
			else
				add(b[edge[i].y], edge[i].x);
		}
	}
	cout << 0;
	return 0;
}

P3386 【模板】二分图最大匹配

#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 510;
int uN, vN;
int g[MAXN][MAXN];
int linker[MAXN];
bool vis[MAXN];
bool dfs(int u)
{
	for(int v=1;v<=vN;v++)
		if (g[u][v] && !vis[v])
		{
			vis[v] = true;
			if (linker[v] == -1 || dfs(linker[v]))
			{
				linker[v] = u;
				return true;
			}
		}
	return false;
}
int hungary()
{
	int res = 0;
	memset(linker, -1, sizeof(linker));
	for (int u = 1; u <= uN; u++)
	{
		memset(vis, false, sizeof(vis));
		if (dfs(u))res++;
	}
	return res;
}
int main()
{
	int e;
	cin >> uN >> vN>>e;
	int u, v;
	while (e--)
	{
		cin >> u >> v;
		g[u][v] = 1;
	}
	cout << hungary();
}

P1129 [ZJOI2007] 矩阵游戏

此题,可以将行看成x,列看成y,维护一张二分图,如果一个点是黑点,则从这个点的x连到对应的y,可以注意到,无论怎么行交换列交换,二分图的匹配情况是不变的,因此可以任意交换,使得,某一条线的xy相同,因此,只要不重点的所有线的数,即最大匹配数,能够等于n,可以通过交换,使得对角线的数都是1,即符合本题要求。此题就转化成了求二分匹配的题了。

#include<iostream>
#include<string.h>
using namespace std;
const int MAXN = 203;
int uN, vN;
int g[MAXN][MAXN];
int linker[MAXN];
bool vis[MAXN];
bool dfs(int u)
{
	for(int v=0;v<vN;v++)
		if (g[u][v] && !vis[v])
		{
			vis[v] = true;
			if (linker[v] == -1 || dfs(linker[v]))
			{
				linker[v] = u;
				return true;
			}
		}
	return false;
}
int hungary()
{
	int res = 0; memset(linker, -1, sizeof(linker));
	for (int u = 0; u < uN; u++)
	{
		memset(vis, false, sizeof(vis));
		if (dfs(u))res++;
	}
	return res;
}
int main()
{
	int m;
	cin >> m;
	while (m--)
	{
		cin >> uN;
		vN = uN;
		memset(g, 0, sizeof(g));
		for (int i = 0; i < uN; i++)
			for (int j = 0; j < uN; j++)
			{
				cin >> g[i][j];
			}
		if (hungary() == uN)
			cout << "Yes";
		else
			cout << "No";
		if (m != 0)cout << endl;
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值