[蓝桥杯 2020 省 AB2] 回文日期
题目描述
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 yyyymmdd
的格式写成一个
8
8
8 位数是 20200202
,恰好是一个回文数。我们称这样的日期是回文日期。
有人表示 20200202
是“千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202
即 2021 年 12 月 2 日。
也有人表示 20200202
并不仅仅是一个回文日期,还是一个 ABABBABA
型的回文日期。对此小明也不认同,因为大约
100
100
100 年后就能遇到下一个 ABABBABA
型的回文日期:21211212
即 2121 年12 月12 日。算不上“千年一遇”,顶多算“千年两遇”。
给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA
型的回文日期各是哪一天。
输入格式
输入包含一个八位整数 N N N,表示日期。
输出格式
输出两行,每行
1
1
1 个八位数。第一行表示下一个回文日期,第二行表示下
一个 ABABBABA
型的回文日期。
样例 #1
样例输入 #1
20200202
样例输出 #1
20211202
21211212
提示
对于所有评测用例, 10000101 ≤ N ≤ 92200229 10000101 \le N \le 92200229 10000101≤N≤92200229,保证 N N N 是一个合法日期的 8 8 8 位数表示。
蓝桥杯 2020 第二轮省赛 A 组 G 题(B 组 G 题)。
思路:其实没啥思路,就是考验代码实现的基本功
AC Code
#include <bits/stdc++.h>
#define endl '\n'
#define ll long long
#define int long long
#define pii pair<int, int>
#define pb push_back
#define gcd __gcd
#define lcm(a, b) (a * b) / gcd(a, b)
#define all(a) a.begin(), a.end()
#define mem(a, x) memset(a, x, sizeof(a))
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define setbits(x) __builtin_popcount(x)
#define zrobits(x) __builtin_ctzll(x)
#define mod 100000007
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
// 判断是否是月份
bool ismonth(int x)
{
return x >= 1 && x <= 12;
}
// 判断是否是闰年
bool isleap(int x)
{
return x % 400 == 0 || (x % 4 == 0 && x % 100 != 0);
}
// 判断是否是合法日期
bool isdate(int y, int m, int d)
{
if (m == 2)
{
if (isleap(y))
{
return d >= 1 && d <= 29;
}
else
{
return d >= 1 && d <= 28;
}
}
else if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
{
return d >= 1 && d <= 31;
}
else
{
return d >= 1 && d <= 30;
}
}
// 检查是否是回文数
bool ishuiwen(string s)
{
int n = s.size();
for (int i = 0; i < n / 2; i++)
{
if (s[i] != s[n - i - 1])
{
return false;
}
}
return true;
}
// 判断ABABBABA
bool isABABBABA(string s)
{
if (ishuiwen(s))
{ // 首先它得是个回文数
// 接下来只需判断前4位是否合法
if (s[0] != s[2] || s[1] != s[3] || s[0] == s[1])
return false;
// 结合ABABBABA思考
return true;
}
return false;
}
signed main(void)
{
ios::sync_with_stdio(false), cin.tie(nullptr);
string s;
int n;
cin >> n;
int flag1 = 0;
f(i, n + 1, 99999999)
{
s = to_string(i);
int date = i;
int year = date / 10000;
int month = date % 10000 / 100;
int day = date % 100; // 分离年月日
// 检查是否合法
if (!ismonth(month) || !isdate(year, month, day))
{
continue;
}
if (ishuiwen(s) && !flag1)
{
cout << i << endl;
flag1 = 1;
}
if (isABABBABA(s) && flag1)
{
cout << i << endl;
return 0;
}
}
return 0;
}
[HNOI2004] 打鼹鼠
题目描述
鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的。根据这个特点阿牛编写了一个打鼹鼠的游戏:在一个 n × n n \times n n×n 的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气。你可以控制一个机器人来打鼹鼠,如果 i i i 时刻鼹鼠在某个网格中出现,而机器人也处于同一网格的话,那么这个鼹鼠就会被机器人打死。而机器人每一时刻只能够移动一格或停留在原地不动。机器人的移动是指从当前所处的网格移向相邻的网格,即从坐标为 ( i , j ) (i, j) (i,j) 的网格移向 ( i − 1 , j ) , ( i + 1 , j ) , ( i , j − 1 ) , ( i , j + 1 ) (i-1, j), (i+1, j), (i, j-1), (i, j+1) (i−1,j),(i+1,j),(i,j−1),(i,j+1) 四个网格,机器人不能走出整个 n × n n \times n n×n 的网格。游戏开始时,你可以自由选定机器人的初始位置。
现在知道在一段时间内,鼹鼠出现的时间和地点,请编写一个程序使机器人在这一段时间内打死尽可能多的鼹鼠。
输入格式
第一行为 n , m n, m n,m( n ≤ 1000 n \le 1000 n≤1000, m ≤ 10 4 m \le {10}^4 m≤104),其中 m m m 表示在这一段时间内出现的鼹鼠的个数,接下来的 m m m 行中每行有三个数据 t i m e , x , y \mathit{time}, x, y time,x,y 表示在游戏开始后 t i m e \mathit{time} time 个时刻,在第 x x x 行第 y y y 个网格里出现了一只鼹鼠。 t i m e \mathit{time} time 按递增的顺序给出。注意同一时刻可能出现多只鼹鼠,但同一时刻同一地点只可能出现一只鼹鼠。
输出格式
仅包含一个正整数,表示被打死鼹鼠的最大数目。
样例 #1
样例输入 #1
2 2
1 1 1
2 2 2
样例输出 #1
1
思路:我们可以考察作为终点的结果,即设dp[i]是以i为终点的最大数量
AC Code
#include <bits/stdc++.h>
#include <stack>
#define ll long long
#define int long long
#define pii pair<int, int>
#define pb push_back
#define gcd __gcd
#define lcm(a, b) (a * b) / gcd(a, b)
#define all(a) a.begin(), a.end()
#define mem(a, x) memset(a, x, sizeof(a))
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define setbits(x) __builtin_popcount(x)
#define zrobits(x) __builtin_ctzll(x)
#define mod 100000007
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int n, m;
struct node
{
int time;
int x;
int y;
};
struct node a[maxn];
int dp[maxn] = {0};//dp[i]表示以第i个时刻为终点打到的最多老鼠数
// 计算哈曼顿距离
int ham(int x1, int y1, int x2, int y2)
{
return abs(x1 - x2) + abs(y1 - y2);
}
signed main(void)
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> n >> m;
f(i, 1, m)
{
cin >> a[i].time >> a[i].x >> a[i].y;
dp[i]=1;//初始化,保证每次至少都能打到一个
}
f(i, 1, m)
{
f(j, 1, i - 1)
{
if (ham(a[i].x, a[i].y, a[j].x, a[j].y) <= a[i].time - a[j].time)
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int ans = 0;
f(i, 1, m)
{
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}
快速求和
题目背景
2023-10-08 update: 新增两组 hack。
2023-12-16 update: 新增两组 hack。
题目描述
给定一个数字字符串,用最小次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在里面要的所有加号都插入后,就像做普通加法那样来求值。
例如,考虑字符串12
,做
0
0
0 次加法,我们得到数字
12
12
12。如果插入
1
1
1 个加号,我们得到
3
3
3,因此,这个例子中,最少用
1
1
1 次加法就得到数字
3
3
3。
再举一例,考虑字符串303
和目标数字
6
6
6,最佳方法不是3+0+3
。而是3+03
。能这样做是因为一个数的前导
0
0
0 不会改变它的大小。
输入格式
第一行:一个字符串 s s s。
第二行:一个整数 n n n。
输出格式
一行一个整数表示最少的加法次数让 s s s 等于 n n n。如果怎么做都不能让 s s s 等于 n n n ,则输出 − 1 -1 −1。
样例 #1
样例输入 #1
99999
45
样例输出 #1
4
提示
数据规模与约定
对于 100 % 100\% 100% 的数据,保证 1 ≤ len ( s ) ≤ 40 1\le \operatorname{len}(s)\le40 1≤len(s)≤40, 1 ≤ n ≤ 1 0 5 1 \leq n\le10^5 1≤n≤105。
思路:这是动态规划题单里的,但是一开始觉得,记忆化dfs也行,但是他就是卡着几个测试点过不了,没办法,后面改用了迭代加深搜索,枚举答案,还加了卡时,真的心疲力尽
AC Code
#include <bits/stdc++.h>
#define ll long long
#define int long long
#define pii pair<int, int>
#define pb push_back
#define gcd __gcd
#define lcm(a, b) (a * b) / gcd(a, b)
#define all(a) a.begin(), a.end()
#define mem(a, x) memset(a, x, sizeof(a))
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define setbits(x) __builtin_popcount(x)
#define zrobits(x) __builtin_ctzll(x)
#define mod 100000007
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
char s[100];
int n;
int len;
int ans;
void dfs(int deep, int cnt, int x1, int la)
{
if ((double)clock() / CLOCKS_PER_SEC > 0.9)
{
cout << -1;
exit(0);
}
if (x1 > n)
{
return;
}
if (deep == len + 1)
{
if (x1 == n)
{
cout << ans << endl;
exit(0);
}
return;
}
if (cnt > 0)
{
dfs(deep + 1, cnt - 1, x1 + s[deep] - '0', s[deep] - '0');
}
dfs(deep + 1, cnt, x1 - la + la * 10 + s[deep] - '0', la * 10 + s[deep] - '0');
return;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> (s + 1) >> n;
len = strlen(s + 1);
for (ans = 0; ans < len; ans++)
{
dfs(1, ans, 0, 0);
}
cout << -1;
return 0;
}
80 分的Code(如果你知道怎么改进,请教教我,蟹蟹尼)
#include <algorithm>
#include <bits/stdc++.h>
#define ll long long
#define int long long
#define pii pair<int, int>
#define pb push_back
#define gcd __gcd
#define lcm(a, b) (a * b) / gcd(a, b)
#define all(a) a.begin(), a.end()
#define mem(a, x) memset(a, x, sizeof(a))
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define setbits(x) __builtin_popcount(x)
#define zrobits(x) __builtin_ctzll(x)
#define mod 100000007
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
string s;
int len;
int n;
bool vis[1005] = {false};
int ans = INF;
int dp[maxn][50];
void dfs(int x1, int deep, int cnt, int la)
{
if ((double)clock() / CLOCKS_PER_SEC > 0.9)
{
cout << -1;
exit(0);
}
if (x1 > n || cnt >= ans) // 剪枝条件
{
return;
}
if (dp[x1][deep] != -1 && dp[x1][deep] <= cnt) // 如果已经计算过并且结果更优,直接返回
{
return;
}
dp[x1][deep] = cnt; // 更新记忆化数组
if (deep == len)
{
if (x1 == n)
{
ans = min(ans, cnt);
}
return;
}
f(i, 0, len - 1)
{
if (vis[i])
{
continue;
}
vis[i] = true;
if (cnt < len)
{
dfs(x1 + s[i] - '0', deep + 1, cnt + 1, s[i] - '0');
}
dfs(x1 - la + la * 10 + s[i] - '0', deep + 1, cnt, la * 10 + s[i] - '0');
vis[i] = false;
}
return;
}
signed main(void)
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> s;
cin >> n;
len = s.size();
mem(dp, -1); // 初始化记忆化数组
dfs(0, 0, 0, 0);
if (ans == INF)
{
ans = -1;
}
cout << ans << endl;
return 0;
}
[蓝桥杯 2021 省 B] 杨辉三角形
题目描述
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
1 , 1 , 1 , 1 , 2 , 1 , 1 , 3 , 3 , 1 , 1 , 4 , 6 , 4 , 1 , … 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1, \ldots 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,…
给定一个正整数 N N N,请你输出数列中第一次出现 N N N 是在第几个数。
输入格式
输入一个整数 N N N 。
输出格式
输出一个整数代表答案。
样例 #1
样例输入 #1
6
样例输出 #1
13
提示
对于 20 % 20 \% 20% 的评测用例, 1 ≤ N ≤ 10 1 \leq N \leq 10 1≤N≤10;
对于所有评测用例, 1 ≤ N ≤ 1 0 9 1 \leq N \leq 10^9 1≤N≤109 。
蓝桥杯 2021 第一轮省赛 B 组 H 题。
思路:大概的估计一下,对角线行数K是20左右,然后二分枚举就行了,每一行对角线都是单调不降的,若一个斜行开头的数比 n 大,则答案一定不在该斜行中。
AC Code
#include <bits/stdc++.h>
#define ll long long
#define int long long
#define pii pair<int, int>
#define pb push_back
#define gcd __gcd
#define lcm(a, b) (a * b) / gcd(a, b)
#define all(a) a.begin(), a.end()
#define mem(a, x) memset(a, x, sizeof(a))
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define setbits(x) __builtin_popcount(x)
#define zrobits(x) __builtin_ctzll(x)
#define mod 100000007
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
// 计算组合数
int N;
ll C(ll n, ll m)
{
if (m > n - m)
{
m = n - m;
}
ll ans = 1;
for (ll i = 1; i <= m; i++)
{
ans = ans * (n - i + 1) / i;
}
return ans;
}
signed main(void)
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> N;
if (N == 1)
{
cout << 1 << endl;
return 0;
}
ff(i, 20, 0)
{
int l = 2 * i;
int r = 1e9;
int mid, lim;
while (l <= r)
{
mid = (l + r) >> 1;
lim = C(mid, i);
if (lim == N)
{
cout << (mid + 1) * mid / 2 + i + 1 << endl;
return 0;
}
else if (lim > N)
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
}
return 0;
}