[Week 17] 每日一题(C++,模拟,数学)
[Daimayuan] T1 碰撞2(C++,模拟)
在
我们有一个由 L
和 R
组成的长为 R
代表第 L
代表第
现在所有人开始朝着他们各自面向的方向走,即面向右
例如,当 RRL
时,人们的移动如图。
我们把两个人对向行走到一个位置称为一次碰撞。请问如果人们可以无限走下去,会有人产生碰撞吗?
输入格式
第一行一个整数
接下来
最后一行是一个由 L
和 R
组成的长为
输出格式
如果会有碰撞,输出 Yes
,否则输出 No
。
样例输入 1
3
2 3
1 1
4 1
RRL
样例输出 1
Yes
样例输入 2
2
1 1
2 1
RR
样例输出 2
No
样例输入 3
10
1 3
1 4
0 0
0 2
0 4
3 1
2 4
4 2
4 4
3 3
RLRRRLRLRR
样例输出 3
Yes
数据规模
所有数据保证
解题思路
根据题意,我们很容易就能得出碰撞条件:
(1)同一行中,即
(2)行走方向相反
(3)向右走的人在左边,向左走的人在右边
那么我们的思路形成了:
遍历每一行,找出每一行中的
接下来的问题就是如何存储数据
显然,想要对应每一个
但是我们简单思考一下,发现也没必要为每一个
采用sort
算法,把相同
最后,AC代码如下
#include <iostream>
#include <algorithm>
using namespace std;
const int max_n = 2e5;
const int max_x = 1e9;
const int max_y = 1e9;
const int NaN = 0x3F3F3F3F;
int n;
struct person { int x, y, dirction; }persons[max_n + 1];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> persons[i].x >> persons[i].y;
}
string str;
cin >> str;
for (int i = 0; i < n; i++) {
if (str[i] == 'R') persons[i + 1].dirction = 0;
else persons[i + 1].dirction = 1;
}
sort(persons + 1, persons + 1 + n, [](person p1, person p2) {
return p1.y < p2.y;
});
int line = -1, l = NaN, r = -1;
for (int i = 1; i <= n; i++) {
if (persons[i].y == line) {
if (persons[i].dirction) r = max(r, persons[i].x);
else l = min(l, persons[i].x);
}
else {
if (l < r) {
cout << "Yes" << endl;
return 0;
}
l = NaN; r = -1;
line = persons[i].y;
if (persons[i].dirction) r = max(r, persons[i].x);
else l = min(l, persons[i].x);
}
}
if (l < r) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
[Daimayuan] T2 优美!最长上升子序列(C++,DP)
多组数据。
每组将给定一个数组。派派希望从中选择一个递增的子序列,越长越好。
但派派认为,这样选出来的子序列依然不够「优美」,形式化的讲,派派希望选择的下标(从
其中
请你帮助派派完成任务吧!
注:子序列的含义不再赘述。
输入格式
第一行一个整数
每组数据包含两行,第一行包含一个整数
随后一行,包含
输出格式
对于每组数据,输出一行,包含一个数,表示能选出的「优美」的最长上升子序列长度。
数据规模
,但保证
样例输入
4
4
1 4 6 7
2
2 2
10
1 2 3 4 5 6 7 8 9 10
10
10 9 8 6 5 2 3 1 2 1
样例输出
3
1
4
1
解释:
对于第一组数据,能选择的「优美」最长上升子序列为
对于第三组数组,选择
对于第四组数据,可选择的「优美」最长上升子序列长度为
解题思路
在寻找「优美」的最长上升子序列之前,我们先回顾一下最长上升子序列的寻找
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (num[i] > num[j]) {
pre[i] = max(pre[i], pre[j] + 1);
}
}
}
pre[i]
中存储的是以对应num[i]
结尾的最长上升子序列长度
要想寻找以num[i]
结尾的最长上升子序列,我们只需要找到num[i]
所能接上的最长前缀pre[j]
(
如果想采用同样的思路,我们会遇到一个问题
因为题目要求索引可以逐个整除,但是我们很难找到所有符合要求的索引
虽然向前找很难,但是向后找很容易,例如,对于
所以我们更新一下算法
for (int i = 1; i <= n; i++) {
for (int j = 2 * i; j <= n; j += i) {
if (num[i] < num[j]) {
pre[j] = max(pre[j], pre[i] + 1);
}
}
}
与常规动态规划的更新方式不同,我们不是根据过去的结果推导出当前的值,而是根据当前的结果去推导未来的值
这种方法可行的原理是:在到达pre[i]
之前,我们已经尝试用所有符合条件的pre[j]
(pre[i]
了,从结果来看,这与之前是一致的
然后计算一下时间复杂度
最后,AC代码如下
#include <iostream>
using namespace std;
const int max_t = 100;
const int max_n = 1e6;
const int max_a = 1e9;
int t, n, arr[max_n + 1];
int dp[max_n + 1];
int main() {
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n;
for (int j = 1; j <= n; j++) {
cin >> arr[j];
}
int ans = 1;
for (int j = 1; j <= n; j++) dp[j] = 1;
for (int j = 1; j <= n; j++) {
int z;
for (z = 2 * j; z <= n; z += j) {
if (arr[z] > arr[j]) {
dp[z] = max(dp[z], dp[j] + 1);
ans = max(ans, dp[z]);
}
}
}
cout << ans << endl;
}
return 0;
}
[Daimayuan] T3 巨大的牛棚(C++,矩阵前缀和)
题目描述
农夫约翰想要在他的正方形农场上建造一座正方形大牛棚。他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方。我们假定,他的农场划分成 n * n
的方格。输入数据中包括有树的方格的列表。你的任务是计算并输出,在他的农场中,不需要砍树却能够修建的最大正方形牛棚。牛棚的边必须和水平轴或者垂直轴平行。 考虑下面的方格,它表示农夫约翰的农场,.
表示没有树的方格,#
表示有树的方格
........
.#...#..
........
........
........
..#.....
........
........
那么最大的牛棚是5*5
的。
输入描述
第一行输入一个正整数
输出描述
输出一个正整数代表牛棚的最大边长
样例输入
8 3
2 2
2 6
6 3
样例输出
5
解题思路
可以看出问题具有单调性:牛棚越大,修建的可能性越小;反之,可能性越大
而我们需要找出可能修建的最大的牛棚,所以采用二分法
int bin_search() {
int l = 0, r = w + 1, m;
while (l + 1 != r) {
m = (l + r) / 2;
if (judge(m)) l = m;
else r = m;
}
return l;
}
然后就是如何实现judge
函数的问题了
显然,判断牛棚大小是否可行,需要遍历所有可能的位置,采用二重循环实现
bool judge(int m) {
for (int i = 1; i <= w - m + 1; i++) {
for (int j = 1; j <= w - m + 1; j++) {
if (找到可行位置) return true;
}
}
return false;
}
那么最后一个问题:找到可行位置的条件是什么?
如果一棵一棵树判断,肯定TLE
的飞起,所以需要想一想其他方法
这时候就要提到矩阵前缀和的概念了,这一方法应用于矩阵中,用于降低判断的时间复杂度
规定
其中map
为二维bool
数组,有树为true
,无树为false
接下来介绍另外一个原理:包含排斥原理
首先看一下下面这张图
我们要计算
这就是包含排斥原理的简单理解,也足够我们解决这道题了
应用以上两个概念,我们可以计算出指定区域内的矩阵和,如果和为
最后,AC代码如下
#include <iostream>
using namespace std;
const int max_n = 1e3;
int w, n;
bool map[max_n + 1][max_n + 1];
int pre[max_n + 1][max_n + 1];
bool judge(int m) {
for (int i = 1; i <= w - m + 1; i++) {
for (int j = 1; j <= w - m + 1; j++) {
int x = i + m - 1, y = j + m - 1;
if (!(pre[x][y] - pre[x][j - 1] - pre[i - 1][y] + pre[i - 1][j - 1]))
return true;
}
}
return false;
}
int bin_search() {
int l = 0, r = w + 1, m;
while (l + 1 != r) {
m = (l + r) / 2;
if (judge(m)) l = m;
else r = m;
}
return l;
}
int main() {
cin >> w >> n;
int x, y;
for (int i = 1; i <= n; i++) {
cin >> x >> y;
map[x][y] = true;
}
for (int i = 1; i <= w; i++) {
for (int j = 1; j <= w; j++) {
pre[i][j] = map[i][j] + pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1];
}
}
cout << bin_search() << endl;
return 0;
}
[Daimayuan] T4 高利贷(C++,二分)
贷款机构声称利率按月累计,贷款人在贷款期间每月偿还固定的分期付款金额。
给出小
输入格式
三个用空格隔开的正整数
输出格式
一个实数
数据范围
输入样例1
1000 1200 1
输出样例1
0.200000
输入样例2
1000 100 12
输出样例2
0.029229
样例解释
对于第一组样例,小
对于第二组样例,小
解题思路
首先重新理解一下题意:
我们一共需要还款
于是有
根据这个计算公式,因为题目给出了
如果计算出的
可以看出,二分搜索的算法与这一思路完全吻合
接下来实现代码
题目给出了
double bin_search() {
double l = 0.0, r = max_p, m;
while (r - l > 1e-6) {
m = (l + r) / 2;
if (judge(m)) l = m;
else r = m;
}
return l;
}
关于judge
函数的实现,之前已经说过,这里直接展示代码
bool judge(double p) {
double down = 1 + p;
double sum = 0.0;
for (int i = 0; i < k; i++) {
sum += m / down;
down *= 1 + p;
}
return sum - n > 0;
}
最后,AC代码如下
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
const int max_n = 1e6;
const int max_m = 1e6;
const int max_k = 300;
const int max_p = 5;
int n, m, k;
bool judge(double p) {
double down = 1 + p;
double sum = 0.0;
for (int i = 0; i < k; i++) {
sum += m / down;
down *= 1 + p;
}
return sum - n > 0;
}
double bin_search() {
double l = 0.0, r = max_p, m;
while (r - l > 1e-6) {
m = (l + r) / 2;
if (judge(m)) l = m;
else r = m;
}
return l;
}
int main() {
cin >> n >> m >> k;
cout << setiosflags(ios::fixed) << setprecision(6) << bin_search() << endl;
return 0;
}
[Daimayuan] T5 背包(C++,模拟)
请你帮
输入格式
第一行给出测试样例个数
对于每一个样例:
第一行给出一个
第二行给出一个序列
输出格式
如果有请输出"
数据范围
样例输入1
3
1 3
3
6 2
19 8 19 69 9 4
7 12
1 1 1 17 1 1 1
样例输出
YES
NO
YES
解题思路
学过背包问题的人不要被标题给误导了
我们回归题意,重新出发
我们把物品分为三类:
(1)如果输入的物品体积大于背包容量,对我们来说就没有意义
(2)如果输入物品体积
(3)如果输出物品体积
对于第三类,不必担心物品总体积会直接超过
所以我们就可以在常数的时间复杂度下解决本题~
最后,AC代码如下
#include <iostream>
using namespace std;
const int max_t = 1e4;
const int max_n = 2e5;
const long long max_w = 1e18;
const int max_item = 1e9;
long long t, n, w;
int main() {
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n >> w;
long long item, sum = 0, ans = 0;
for (int i = 0; i < n; i++) {
cin >> item;
if (item <= w) {
if (item >= (w + 1) / 2) ans = 1;
else {
sum += item;
if (sum >= (w + 1) / 2) ans = 1;
}
}
}
if (ans) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
[Daimayuan] T6 三回文序列(C++,前缀和)
给定一个长度为
我们定义三回文序列是形如
现在,希望你找到序列
注意,
输入格式
第一行给出一个数字
对于每一个测试用例
第一行给出序列的长度
第二行给出序列
输出格式
对于每一个测试用例,输出最长的三回文序列的长度。
数据范围
样例输入
6
8
1 1 2 2 3 2 1 1
3
1 3 3
4
1 10 10 1
1
26
2
2 1
3
1 1 1
样例输出
7
2
4
1
1
3
解题思路
通过样例,我们注意到三回文序列不一定是连续序列
也就是说1 1 2 2 3 2 1 1
的最长三回文序列是1 1 2 2 2 1 1
要想找出最长的三回文序列,我们就需要找出所有三回文序列
首先想到动态规划,但是很难找出最优子结构
然后因为是序列问题,我们想到了前缀和
基本思路:尝试
这么直接看可能不太容易懂,可以暂时理解一下大概
由这个思路很容易看出我们要采用三重循环来实现
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= a的总数; k1++) {
for (int b = 1; b <= 26; b++) {
...
}
}
}
对应于每一个
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= a的总数; k1++) {
int l = 左索引, r = 右索引;
if (l > r) break;
for (int b = 1; b <= 26; b++) {
int k2 = [l+1, r-1]范围内b的数量;
max_len = max(max_len, k1 * 2 + k2);
}
}
}
好了,我们的伪代码写完了,接下来是如何把对应部分替换为真正的代码
可以看出,我们有两个要求:
(1)指定
(2)指定某个区间范围,我们要求返回该范围内指定元素
实现方式是维护两个数组sum[26][n], indices[26][n]
sum[i][j]
存储的前j
个元素的i
的数量
indices[i][j]
存储的是i
的前缀和为j
时在序列中的下标
如何使用这两个数组呢?
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= sum[a][n]; k1++) {
int l, r;
if (k1) {
l = indices[a][k1];
r = indices[a][sum[a][n] - k1 + 1];
}
else {
l = 0;
r = n + 1;
}
if (l > r) break;
else if (l == r) max_len = max(max_len, 2 * k1 - 1);
else {
for (int b = 1; b <= 26; b++) {
int k2 = sum[b][r - 1] - sum[b][l - 1];
max_len = max(max_len, k1 * 2 + k2);
}
}
}
}
最后,AC代码如下
#include <iostream>
using namespace std;
const int max_num = 26;
const int max_len = 2e5;
int sum[max_num + 1][max_len + 1], indices[max_num + 1][max_len + 1];
int main() {
int t, n, x;
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n;
for (int j = 1; j <= n; j++) {
cin >> x;
for (int k = 1; k <= max_num; k++) {
sum[k][j] = sum[k][j - 1];
}
sum[x][j]++;
indices[x][sum[x][j]] = j;
}
int ans = 0;
for (int a = 1; a <= 26; a++) {
for (int k1 = 0; k1 <= sum[a][n]; k1++) {
int l, r;
if (k1) {
l = indices[a][k1];
r = indices[a][sum[a][n] - k1 + 1];
}
else {
l = 0;
r = n + 1;
}
if (l > r) break;
else if (l == r) ans = max(ans, 2 * k1 - 1);
else {
for (int b = 1; b <= 26; b++) {
int k2 = sum[b][r - 1] - sum[b][l];
ans = max(ans, k1 * 2 + k2);
}
}
}
}
cout << ans << endl;
}
return 0;
}
[Daimayuan] T7 简单的异或问题(C++,数学)
有一组整数
输入格式
两个数
输出格式
输出最大的满足条件的
样例输入
2 2
样例输出
3
样例解释
对于样例,我们可以选择
解题思路
首先介绍一下异或和:
异或操作:同则为假,不同为真
1^0=1
0^1=1
0^0=0
1^1=0
给定两个数,这里以
0000 1000
0000 0100
---------
0000 1100
接下来是解题思路:
这里先列出来一部分二进制格式的数字
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15
然后我们可以发现:任意数量大于
再进一步看到:当
那么当
当
AC代码如下
#include <iostream>
using namespace std;
int main() {
long long n, m;
cin >> n >> m;
if (m == 1) {
if (n) cout << 2 << endl;
else cout << 1 << endl;
}
else {
if (!n) cout << (1LL << m) << endl;
else cout << ((1LL << m) - 1) << endl;
}
return 0;
}
[Daimayuan] T8 子串的循环挪动(C++,模拟)
给出一个字符串
字符串的循环挪动操作:将最后一个字符移到第一个字符的位置,并且将其他所有字符向右移一个位置。
比如:如果字符串 abacaba
,一个任务为 abbacaa
。接下来一个任务为 baabcaa
。
输入格式
第一行一个字符串
第二行一个整数
接下来
输出格式
输出执行了
样例输入
abacaba
2
3 6 1
1 4 2
样例输出
baabcaa
数据规模
对于所有数据保证,
解题思路:
步骤如下:
(1)对
(2)取出子串尾部
(3)将前
(3)将缓存的字符放回子串首部
(
AC代码如下
#include <iostream>
using namespace std;
const int max_len = 1e4;
char str[max_len + 1];
char buffer[max_len + 1];
int main() {
char c = '\0';
int len = 0;
while ((c = getchar()) != '\n' && c != '\r') {
str[len++] = c;
}
str[len] = '\0';
int t, l, r, k;
cin >> t;
for (int i = 0; i < t; i++) {
cin >> l >> r >> k;
k %= r - l + 1; l--; r--;
for (int j = r - k + 1; j <= r; j++) {
buffer[j] = str[j];
}
for (int j = r - k; j >= l; j--) {
str[j + k] = str[j];
}
for (int j = r - k + 1, idx = l; j <= r; j++, idx++) {
str[idx] = buffer[j];
}
}
cout << str << endl;
return 0;
}
[Daimayuan] T9 弗拉德和糖果 II(C++,数学)
不久前,弗拉德过生日,他收到了一包糖果。有
弗拉德决定每次只吃一个糖果。为了从吃东西中获得最大的乐趣,弗拉德不想连续吃两个相同类型的糖果。
帮助他弄清楚他是否可以在不连续吃两个相同的糖果的情况下吃掉所有的糖果。
简而言之,给定
输入格式:
第一行,包含一个整数
输出格式:
输出一行,如果存在,输出YES
,否则输出NO
样例输入
2
1 1
样例输出
YES
说明
只有两种情况:
1 2
2 1
无论先吃哪种糖果,都能吃完且不连续吃相同类型的糖果
数据限制
对于
解题思路:
因为要求不能连续吃相同的糖果,所以我们预先把相同糖果间留出空隙
我们只关心糖果之间的空隙能否被填充,并不关心之前或者之后有没有其他糖果,也不关心空隙之间到底有多少颗糖果
那么假设在吃
(1)如果有
(2)但是如果条件不成立,那么我们就会剩下
(3)然后我们接收下一个输入
(4)在顺利吃掉
注:当sum > max_a
的时候,无论接下来的输入糖果数量是多少,我们都可以吃掉,所以不用继续累计
AC代码如下
#include <stdio.h>
#include <stdlib.h>
const int max_n = 5e6;
const long long max_a = 2ll << 30;
int main() {
int n;
scanf("%d", &n);
long long a, sum = 0, buffer = 0;
for (int i = 0; i < n; i++) {
scanf("%lld", &a);
if (sum <= max_a) {
if (!buffer) {
if (sum >= a - 1) sum += a;//(1)
else {//(2)
sum = sum * 2 + 1;
buffer = a - sum - 1;
}
}
else {//(3)
if (a >= buffer) {
sum += buffer;
if (sum >= a - 1) {
sum += a;
buffer = 0;//return (1)
}
else {
sum = sum * 2 + 1;
buffer = a - sum - 1;//return (3)
}
}
else {
buffer -= a;
sum += 2 * a;
}
}
}
}
if (sum > max_a || !buffer) printf("YES");
else printf("NO");
return 0;
}
[Daimayuan] T10 上帝的集合(C++,线性表)
题目描述
现在上帝有一个空集合,现在他命令你为他执行下列三种操作
操作
操作
操作
输入描述
第一行输入一个整数
接下来的
输出描述
如果
样例输入
7
1 2
1 1
3
1 3
2 5
3
3
样例输出
1
7
8
数据范围
解题思路:
采用优先队列维护集合
对于集合的更新操作,维护一个变量add
,每次插入前将元素先减去add
,每次输出前先将元素加上add
后输出即可
AC代码如下
#include <stdio.h>
#include <stdlib.h>
#include <queue>
using namespace std;
priority_queue<long long, vector<long long>, greater<long long>>q;
int main() {
int n;
scanf("%d", &n);
long long choice, x, add = 0;
for (int i = 0; i < n; i++) {
scanf("%lld", &choice);
switch (choice) {
case 1:
scanf("%lld", &x);
q.push(x - add);
break;
case 2:
scanf("%lld", &x);
add += x;
break;
case 3:
printf("%lld\n", q.top() + add);
q.pop();
break;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库