CSP-S 2022 T1假期计划

CSP-S 2022 T1假期计划

先思考暴力做法,题目需要找到四个不相同的景点,那我们就枚举这四个景点,判断它们之间的距离是否符合条件,条件是任意两个点之间的距离是否大于 k k k,所以我们需要求出任意两点之间的距离。常用的 D i j k s t r a Dijkstra Dijkstra和 S P F A SPFA SPFA都是单源最短路,也就是只能求一个点到其它点的距离,而 F l o y e d Floyed Floyed可以求任意两个点之间的最短路,虽然其时间复杂度是 O ( n 3 ) O(n^3) O(n3),但对于这个做法的数据范围是可以接受的。这个做法的时间复杂度为 O ( n 4 ) O(n^4) O(n4)(枚举四个景点),在 k k k较小的情况下可以通过(因为 k k k会影响到循环退出),可以拿到 55 55 55分。

cpp 复制代码
#include <bits/stdc++.h>
#define A 2510

using namespace std;
typedef long long ll;
int n, m, kk, x, y, dis[A][A];
ll a[A], ans;

int main(int argc, char const *argv[]) {
	scanf("%d%d%d", &n, &m, &kk); kk++;
	for (int i = 2; i <= n; i++) scanf("%lld", &a[i]);
	memset(dis, 0x3f, sizeof dis);
	for (int i = 1; i <= n; i++) dis[i][i] = 0;
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &x, &y);
		dis[x][y] = 1; dis[y][x] = 1;
	}
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
	for (int i = 2; i <= n; i++) {
		if (dis[1][i] > kk) continue;
		for (int j = 2; j <= n; j++) {
			if (i == j) continue;
			if (dis[i][j] > kk) continue;
			for (int k = 2; k <= n; k++) {
				if (i == k or j == k) continue;
				if (dis[j][k] > kk) continue;
				for (int l = 2; l <= n; l++) {
					if (i == l or j == l or k == l) continue;
					if (dis[k][l] > kk or dis[l][1] > kk) continue;
					ans = max(ans, a[i] + a[j] + a[k] + a[l]);
				}
			}
		}
	}
	printf("%lld\n", ans);
}

比较特殊的数据点是当 k = 0 k=0 k=0时,也就是挑选的 4 4 4个景点必须都相邻,直接通过 d f s dfs dfs搜索所有的情况,如果遍历到了家( 1 1 1号点)并且已经路过了 4 4 4个不同的节点,这就是一条可行的路径,可以更新答案。 k = 0 k=0 k=0共有 9 9 9个测试点,共 45 45 45分。

cpp 复制代码
#include <bits/stdc++.h>
#define A 2510

using namespace std;
int n, m, k, a[A], ans;
bool vis[A], mp[A][A];
void dfs(int now, int sum, int left) {
	if (now == 1 and left == 0) {
		ans = max(ans, sum);
		return;
	}
	else if (left == 0) return;
	for (int i = 1; i <= n; i++) {
		if (!mp[now][i] or vis[i]) continue;
		vis[i] = 1;
		dfs(i, sum + a[i], left - 1);
		vis[i] = 0;
	}
}

int main() {
	cin >> n >> m >> k;
	for (int i = 2; i <= n; i++) cin >> a[i];
	while (m--) {
		int x, y;
		cin >> x >> y;
		mp[x][y] = 1; mp[y][x] = 1;
	}
	dfs(1, 0, 5);
	cout << ans << endl;
}

这个写法可以通过 k = 0 k=0 k=0的所有特殊情况,和第一个暴力结合一下,可以拿到 70 70 70分。

cpp 复制代码
#include <bits/stdc++.h>
#define A 2510

using namespace std;
typedef long long ll;
int n, m, kk, x, y, dis[A][A];
ll a[A], ans;
bool vis[A], mp[A][A];
void dfs(int now, ll sum, int left) {
	if (now == 1 and left == 0) {
		ans = max(ans, sum);
		return;
	}
	else if (left == 0) return;
	for (int i = 1; i <= n; i++) {
		if (!mp[now][i] or vis[i]) continue;
		vis[i] = 1;
		dfs(i, sum + a[i], left - 1);
		vis[i] = 0;
	}
}

int main(int argc, char const *argv[]) {
	scanf("%d%d%d", &n, &m, &kk);
	if (kk == 0) {
		for (int i = 2; i <= n; i++) scanf("%lld", &a[i]);
		while (m--) {
			scanf("%d%d", &x, &y);
			mp[x][y] = 1; mp[y][x] = 1;
		}
		dfs(1, 0, 5);
		printf("%lld\n", ans);
		return 0;
	}
	kk++;
	for (int i = 2; i <= n; i++) scanf("%lld", &a[i]);
	memset(dis, 0x3f, sizeof dis);
	for (int i = 1; i <= n; i++) dis[i][i] = 0;
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &x, &y);
		dis[x][y] = 1; dis[y][x] = 1;
	}
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
	for (int i = 2; i <= n; i++) {
		if (dis[1][i] > kk) continue;
		for (int j = 2; j <= n; j++) {
			if (i == j) continue;
			if (dis[i][j] > kk) continue;
			for (int k = 2; k <= n; k++) {
				if (i == k or j == k) continue;
				if (dis[j][k] > kk) continue;
				for (int l = 2; l <= n; l++) {
					if (i == l or j == l or k == l) continue;
					if (dis[k][l] > kk or dis[l][1] > kk) continue;
					ans = max(ans, a[i] + a[j] + a[k] + a[l]);
				}
			}
		}
	}
	printf("%lld\n", ans);
}

我们要找的一个路径是 1 → A → B → C → D → 1 1\to A\to B\to C\to D\to 1 1→A→B→C→D→1,可以发现其中 A A A和 D D D、 B B B和 C C C有一些共同之处: A A A和 D D D的两端一定是起点 1 1 1和一个其它的点,由于道路是双向的,所以可以说 A A A、 D D D这两个点是等价的; B B B和 D D D的两端一定是非起点,可以说 B B B、 C C C这两个点是等价的。这样一来中间不同的四个点被压缩成了两个点。

用一个双重循环枚举 A A A和 B B B, A A A、 B B B点的特征是 d i s [ 1 ] [ A ] < k dis[1][A]<k dis[1][A]<k且 d i s [ A ] [ B ] < k dis[A][B]<k dis[A][B]<k,同时满足条件的点 A A A也对应着点 D D D,点 B B B对应着点 C C C。对于所有的点 B B B,找到所有符合条件的点 A A A,符合条件的点 A A A可能有很多,我们只需要存值最大的三个就可以。

为什么是存三个点?

对于路径 1 → A → B → C → D → 1 1\to A\to B\to C\to D\to 1 1→A→B→C→D→1,假设枚举点 B B B时找到了点 j j j作为 A A A点,枚举点 C C C时找到了点 k k k作为 D D D点,那么 k = j k=j k=j、 k = B k=B k=B都是有可能发生的,所以要存三个点以防重复。

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define A 2510
vector<int> e[A];
ll dis[A][A], ans, a[A];
bool vis[A];
int n, m, k;
void bfs(int start) {
	memset(vis, 0, sizeof vis); vis[start] = 1;
	queue<int> q; q.push(start);
	while (!q.empty()) {
		int fr = q.front(); q.pop();
		for (int i = 0; i < (int)e[fr].size(); i++) {
			int ca = e[fr][i];
			if (vis[ca]) continue;
			vis[ca] = 1;
			if (dis[start][ca] > dis[start][fr] + 1) {
				dis[start][ca] = dis[start][fr] + 1;
				q.push(ca);
			}
		}
	}
}
set<pair<ll, int> > s[A];

int main(int argc, char const *argv[]) {
	scanf("%d%d%d", &n, &m, &k); k++;
	for (int i = 2; i <= n; i++) scanf("%lld", &a[i]);
	while (m--) {
		int x, y; scanf("%d%d", &x, &y);
		e[x].push_back(y); e[y].push_back(x);
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			dis[i][j] = INT_MAX;
			if (i == j) dis[i][j] = 0;
		}
	for (int i = 1; i <= n; i++) bfs(i);
	for (int i = 2; i <= n; i++) {
		for (int j = 2; j <= n; j++)
			if (i != j and dis[j][i] <= k and dis[1][j] <= k) {
				s[i].insert(make_pair(a[j], j));
				if (s[i].size() > 3) s[i].erase(s[i].begin());
			}
	}
	for (int i = 2; i <= n; i++)
		for (int j = 2; j <= n; j++) {
			if (dis[i][j] > k or i == j) continue;
			for (auto k : s[i]) {
				if (k.second == i or k.second == j) continue;
				for (auto l : s[j]) {
					if (l.second == i or l.second == j or l.second == k.second) continue;
					ans = max(ans, a[i] + a[j] + a[l.second] + a[k.second]);
				}
			}
		}
	cout << ans << endl;
}
相关推荐
Coovally AI模型快速验证16 分钟前
MMYOLO:打破单一模式限制,多模态目标检测的革命性突破!
人工智能·算法·yolo·目标检测·机器学习·计算机视觉·目标跟踪
可为测控1 小时前
图像处理基础(4):高斯滤波器详解
人工智能·算法·计算机视觉
Milk夜雨1 小时前
头歌实训作业 算法设计与分析-贪心算法(第3关:活动安排问题)
算法·贪心算法
BoBoo文睡不醒1 小时前
动态规划(DP)(细致讲解+例题分析)
算法·动态规划
apz_end2 小时前
埃氏算法C++实现: 快速输出质数( 素数 )
开发语言·c++·算法·埃氏算法
仟濹3 小时前
【贪心算法】洛谷P1106 - 删数问题
c语言·c++·算法·贪心算法
CM莫问3 小时前
python实战(十五)——中文手写体数字图像CNN分类
人工智能·python·深度学习·算法·cnn·图像分类·手写体识别
sz66cm4 小时前
LeetCode刷题 -- 45.跳跃游戏 II
算法·leetcode
Amor风信子4 小时前
华为OD机试真题---战场索敌
java·开发语言·算法·华为od·华为
old_power4 小时前
【PCL】Segmentation 模块—— 基于图割算法的点云分割(Min-Cut Based Segmentation)
c++·算法·计算机视觉·3d