GDUT22级寒假训练专题三

文章介绍了如何使用最小生成树算法解决平面坐标上的城市连接问题,以及如何通过单源最短路算法解决无向图中两点间的最短路径。此外,还探讨了二分图的最大独立集问题,以及如何运用匈牙利算法计算最大点匹配以满足投票者需求。
摘要由CSDN通过智能技术生成

题解

F - Built?

传送门

题意

  平面上有 N N N个城市。第 i i i个城市的坐标为 ( x i , y i ) (x_i,y_i) (xi,yi)( x , y x,y x,y均为整数)同一个坐标上可能有多个城市。在坐标为 ( a , b ) (a,b) (a,b)的城市和坐标为 ( c , d ) (c,d) (c,d)的城市间建造一条道路需要 m i n ( ∣ a − c ∣ , ∣ b − d ∣ ) min(∣a−c∣,∣b−d∣) min(ac,bd)元。只能在城市与城市间建造道路。 要使任意两个城市之间有直接或间接道路相连,最少需要多少元?
2 ≤ N ≤ 1 0 5 , 0 ≤ x i , y i ≤ 1 0 9 2≤N≤10^5,0≤x_i ,y_i ≤10^9 2N1050xi,yi109

思路

  题目是最小生成树,但是N的范围较大,暴力建边显然会超时。
  观察到每一条边的长度是两点 x x x之差和 y y y之差的最小值,可以将 x x x之差和 y y y之差分开建边,这样 N N N个城市按 x x x y y y大小排序后建边只需建 2 ( N − 1 ) 2(N-1) 2(N1)条边即可。
  建完边后只需用喜欢的最小生成树算法做即可。

代码

#include <bits/stdc++.h>
using namespace std;
int fa[100005];

int fa_find(int x)
{
	if (fa[x] == x)
		return x;
	else
		return fa[x] = fa_find(fa[x]);
}

void fa_add(int x, int y)
{
	fa[fa_find(x)] = fa_find(y);
}
struct edge
{
	int x, y, v;
}e[100005], ae[200005];
int cmpx(edge a, edge b)
{
	return a.x < b.x;
}

int cmpy(edge a, edge b)
{
	return a.y < b.y;
}

int cmpv(edge a, edge b)
{
	return a.v < b.v;
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 0; i <= n; i++)
		fa[i] = i;
	for (int i = 1; i <= n; i++)
	{
		e[i].v = i;
		scanf("%d%d", &e[i].x, &e[i].y);
	}
	sort(e + 1, e + n + 1, cmpx);
	for (int i = 1; i < n; i++)
	{
		ae[i].x = e[i].v;
		ae[i].y = e[i + 1].v;
		ae[i].v = e[i + 1].x - e[i].x;
	}
	sort(e + 1, e + n + 1, cmpy);
	for (int i = 1; i < n; i++)
	{
		ae[i + n - 1].x = e[i].v;
		ae[i + n - 1].y = e[i + 1].v;
		ae[i + n - 1].v = e[i + 1].y - e[i].y;
	}
	sort(ae + 1, ae + 2 * n - 1, cmpv);//Kruskal
	long long ans = 0;
	for (int i = 1; i < 2 * n - 1; i++)
		if (fa_find(ae[i].x) != fa_find(ae[i].y))
		{
			fa_add(ae[i].x, ae[i].y);
			ans += ae[i].v;
		}
	printf("%lld\n", ans);
	return 0;
}

I - 单源最短路

传送门

题意

  给一个 n n n个点 m m m条边的无向图,求 s s s点到 t t t点的最短路。
1 ≤ n ≤ 2500 , 1 ≤ m ≤ 6200 , 1 ≤ s , t ≤ n 1≤n≤2500,1≤m≤6200,1≤s,t≤n 1n25001m62001stn

思路

  单源最短路模板题。题目数据量小,用邻接表+spfa解决。

代码

#include <bits/stdc++.h>
using namespace std;

int n, m, s, t;
const int maxn = 2505;
const int maxm = 6205;
int vis[maxn] = { 0 };
long long dis[maxn];
vector<pair<int,int>> e[maxn];//邻接表

void spfa()
{
	for (int i = 1; i <= n; i++)
		dis[i] = INT64_MAX;//s到i点距离初始化
	deque<int> list;//STL的双向队列
	list.push_back(s), dis[s] = 0, vis[s] = 1;//出发点入队
	while (!list.empty())
	{
		int x = list[0];
		for (int i = 0; i < e[x].size(); i++)
		{
			int nx = e[x][i].first, nd = e[x][i].second;
			if (dis[nx] > dis[x] + nd)//如果有最短路就更改
			{
				dis[nx] = dis[x] + nd;
				if (vis[nx] == 0)//未入队则入队
				{
					vis[nx] = 1;//标记入队
					list.push_back(nx);
				}
			}
		}
		list.pop_front();
		vis[x] = 0;//标记出队
	}
	return;
}

int main()
{
	cin >> n >> m >> s >> t;
	for (int i = 0; i < m; i++)
	{
		int x, y, d;
		scanf("%d%d%d", &x, &y, &d);
		e[x].push_back(make_pair(y, d));
		e[y].push_back(make_pair(x, d));//无向图双向建边
	}
	spfa();
	cout << dis[t];
	return 0;
}

N - Cat VS Dog

传送门

题意

  有 N N N只猫和 M M M只狗,还有 P P P个投票者,每一个投票者会选择投票一只猫留下并且一只狗淘汰或一只狗留下并且一只猫淘汰,求最多可以使多少个投票者的想法得到满足(即自己喜欢的猫(狗)留了下来且讨厌的狗(猫)被淘汰)。
多组数据输入, N < = 100 , M < = 100 , P < = 500. 多组数据输入,N <= 100, M <= 100 ,P <= 500. 多组数据输入,N<=100,M<=100,P<=500.

思路

  首先可以看出,投票者分为两种,喜欢猫的人和喜欢狗的人,两种人构成了一个二分图。一个喜欢人某只猫(狗)而另一个人讨厌这只猫(狗),这两个人的想法不可能同时满足。我们考虑将这两个人建边,计算这个这个二分图的最大独立集,集中的便是可以被满足的最多的人。
  二分图最大独立集等于总点数减最大点匹配,可以使用匈牙利算法计算二分图最大点匹配。

代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 505
vector<int> e[maxn];
int vis[maxn], match[maxn];

//标准的最大点匹配
bool dfs(int x, int flag) {
	for (int i = 0; i < e[x].size(); ++i) {
		int nx = e[x][i];
		if (vis[nx] != flag) {
			vis[nx] = flag;
			if (!match[nx] || dfs(match[nx], flag)) {
				match[nx] = x;
				return 1;
			}
		}
	}
	return 0;
}

int main()
{
	int c, d, v;
	while (cin >> c >> d >> v)
	{
		vector<int> Plc, Pld;
		int vC[maxn], vD[maxn];
		for (int i = 1; i <= v; i++)
		{
			e[i].clear();
			char a, b;
			int x, y;
			cin >> a >> x >> b >> y;
			if (a == 'C')
			{
				Plc.push_back(i);//喜欢猫的人
				vC[i] = x;
				vD[i] = y;
			}
			else
			{
				Pld.push_back(i);//喜欢狗的人
				vC[i] = y;
				vD[i] = x;
			}
		}
		//喜欢猫的人和喜欢狗的人一一比较是否冲突
		for (int i = 0; i < Plc.size(); i++)
		{
			for (int j = 0; j < Pld.size(); j++)
			{
				int x = Plc[i], y = Pld[j];
				if (vC[x] == vC[y] || vD[x] == vD[y])
					e[x].push_back(y);
			}
		}
		int ans = v;
		memset(match, 0, sizeof(match));
		memset(vis, 0, sizeof(vis));//每次vis数组存在的检测都改变,避免反复memset浪费时间
		for (int i = 0; i < Plc.size(); i++)
			if (dfs(Plc[i], i + 1)) ans--;
		cout << ans << endl;
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值