ABC390题解

A

算法标签: 模拟

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 5;

int w[N];

bool check() {
	for (int i = 0; i < N; ++i) {
		if (w[i] != i + 1) return false;
	}
	return true;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	for (int i = 0; i < N; ++i) cin >> w[i];

	int i = 0, j = 1;
	while (j < N) {
		if (w[i] > w[j]) {
			swap(w[i], w[j]);
			if (check()) {
				cout << "Yes" << "\n";
				return 0;
			}
			swap(w[i], w[j]);
		}
		i++, j++;
	}

	cout << "No" << "\n";
	return 0;
}

B

算法标签: 数学

在这里插入图片描述

被坑了, 得开long doublelong \, doublelongdouble精度才够用

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

typedef long double LD;
const int N = 110;
const LD EPS = 1e-18;

int n, w[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	if (n <= 2) {
		cout << "Yes" << "\n";
		return 0;
	}

	for (int i = 1; i <= n; ++i) cin >> w[i];

	int i = 1, j = 2, k = 3;

	while (k <= n) {
		LD val1 = (LD) w[j] * w[j];
		LD val2 = (LD) w[i] * w[k];
		if (fabs(val1 - val2) > EPS) {
			cout << "No" << "\n";
			return 0;
		}
		i++, j++, k++;
	}

	cout << "Yes" << "\n";
	return 0;
}

C

算法标签: 模拟

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

const int N = 1010, INF = 0x3f3f3f3f;

int r, c;
char w[N][N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> r >> c;
	int i1 = INF, j1 = INF, i2 = -INF, j2 = -INF;
	
	for (int i = 1; i <= r; ++i) {
		for (int j = 1; j <= c; ++j) {
			char c;
			cin >> c;
			if (c == '#') {
				i1 = min(i1, i);
				j1 = min(j1, j);
				i2 = max(i2, i);
				j2 = max(j2, j);
			}
			w[i][j] = c;
 		}
	}

	for (int i = i1; i <= i2; ++i) {
		for (int j = j1; j <= j2; ++j) {
			if (w[i][j] == '.') {
				cout << "No" << "\n";
				return 0;
			}
		}
	}

	cout << "Yes" << "\n";
	return 0;
}

D

算法标签: dfsdfsdfs

在这里插入图片描述

思路

异或运算具有交换律, 将nnn个元素进行集合的划分, 但是不考虑顺序, 计算并且去重

#include <iostream>
#include <vector>
#include <unordered_set>

using namespace std;

typedef long long LL;
const int N = 14;

int n;
LL w[N];
unordered_set<LL> s;
vector<LL> cols;  // 当前各堆的石子总数

void dfs(int u, LL curr) {
	if (u == n) {
		s.insert(curr);
		return;
	}

	// 尝试将当前石子放入已有的每一堆
	for (int i = 0; i < cols.size(); ++i) {
		LL val = curr ^ cols[i];
		cols[i] += w[u];
		dfs(u + 1, val ^ cols[i]);
		cols[i] -= w[u];
	}

	// 创建新的一堆
	cols.push_back(w[u]);
	dfs(u + 1, curr ^ w[u]);
	cols.pop_back();
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	for (int i = 0; i < n; ++i) cin >> w[i];

	dfs(0, 0);
	cout << s.size() << "\n";

	return 0;
}

E

算法标签: 二分, 背包问题

在这里插入图片描述

思路

求的是摄入最小值最大的可能值, 考虑二分, 设midmidmid是二分出的答案, 那么需要检查当前摄入方案中每种维生素是否都是≥mid\ge midmid, 可以讲每种食物按照能获取到的维生素进行分类
在这里插入图片描述对于每种维生素食物设置卡路里上限, 假如是X1,X2,X3X_1, X_2, X_3X1,X2,X3, 需要满足X1+X2+X3≤XX_1 + X_2 + X_3 \le XX1+X2+X3X, 在这个条件下进行下面的分析

对于每种维生素设置状态表示f[i][j]f[i][j]f[i][j]表示从前iii个食物中选择, 总卡路里不超过jjj的所有方案中能够获取维生素最多的方案, 也就是背包问题, 如果最多获取到的维生素都小于midmidmid, 说明方案一定不合法, 在满足能够获取的最小维生素是midmidmid的情况下, 检查每个维生素对应的食物至少需要多少卡路里, 如果三个卡路里相加大于XXX说明无解返回falsefalsefalse

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

typedef long long LL;
const int N = 5010, M = 5010, INF = 0x3f3f3f3f;

int n, m;
vector<int> vs[4];
int w[N], c[N];
// f[j]表示总卡路里不超过j的选取方案中维生素含量的最大值
LL f1[M], f2[M], f3[M];

void init() {
	for (int i = 0; i < vs[1].size(); ++i) {
		int k = vs[1][i];
		for (int j = m; j >= c[k]; --j) {
			f1[j] = max(f1[j], f1[j - c[k]] + w[k]);
		}
	}
	for (int i = 0; i < vs[2].size(); ++i) {
		int k = vs[2][i];
		for (int j = m; j >= c[k]; --j) {
			f2[j] = max(f2[j], f2[j - c[k]] + w[k]);
		}
	}
	for (int i = 0; i < vs[3].size(); ++i) {
		int k = vs[3][i];
		for (int j = m; j >= c[k]; --j) {
			f3[j] = max(f3[j], f3[j - c[k]] + w[k]);
		}
	}
}

// 最小维生素含量是mid
bool check(int mid) {
	int c1 = INF, c2 = INF, c3 = INF;
	for (int i = 0; i <= m; ++i) {
		if (f1[i] >= mid) {
			c1 = i;
			break;
		}
	}
	for (int i = 0; i <= m; ++i) {
		if (f2[i] >= mid) {
			c2 = i;
			break;
		}
	}
	for (int i = 0; i <= m; ++i) {
		if (f3[i] >= mid) {
			c3 = i;
			break;
		}
	}

	LL val = (LL) c1 + c2 + c3 - m;
	return val <= 0;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;
	for (int i = 1; i <= n; ++i) {
		int v;
		cin >> v >> w[i] >> c[i];
		vs[v].push_back(i);
	}

	init();

	int l = 0, r = 1e9;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}

	cout << l << "\n";
	return 0;
}

F

算法标签: 容斥原理

在这里插入图片描述

思路

f[L,R]f[L, R]f[L,R]的实际含义就是L,RL, RL,R之间有多少个连续的区间
在这里插入图片描述

例如上面这个例子, f=3f = 3f=3, 假设对于每个段的起始位置设为xxx, 那么xxx是起始位置等价于xxx出现但是x−1x - 1x1未出现
因此本质上求区间中xxx出现但是x−1x - 1x1没出现的数的个数

如何计算每个位置的贡献?
等价于计算在多少个区间[L,R][L, R][L,R]中, AiA_iAi作为某一段的开始位置
区间个数的求和等价于原问题的ansansans

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;
const int N = 3e5 + 10;

int n, w[N];
int l[N], r[N];
// w[i]上一次出现的位置
int vis[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> w[i];

	for (int i = 1; i <= n; ++i) {
		l[i] = max(vis[w[i]], vis[w[i] - 1]) + 1;
		vis[w[i]] = i;
	}

	fill(vis, vis + n + 1, n + 1);
	for (int i = n; i; --i) {
		r[i] = vis[w[i] - 1] - 1;
		vis[w[i]] = i;
	}

	LL ans = 0;
	for (int i = 1; i <= n; ++i) {
		LL cnt1 = i - l[i] + 1;
		LL cnt2 = r[i] - i + 1;
		ans += cnt1 * cnt2;
	}

	cout << ans << "\n";
	return 0;
}

G

算法标签:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值