7.21开始前90道题总结
T8
-
判断一个数x是不是完全平方数。
Tips: sqrt(x)后 平方,看是否与x相等
if ((int)sqrt(n) * (int)sqrt(n) == n)
-
计算一个数x是几位数
(int)(log10(x)) + 1 -
%llu 读入unsigned long long 类型
-
ULL后缀 防止数据溢出
T14 循环找到两个值的最小差值
labs 获取长整数(long类型)的绝对值。
llabs 获取长长整数(long long类型)的绝对值。
//时间复杂度较小
if (n / k > 0) // 消除整除的部分,尽可能地减小剩余值 n 与参数 k 的绝对差值。
n = n - (n / k) * k;
min = n;
while (1)
{
i = llabs(n - k);
if (i < min) //判断条件
min = i;
else
break;
n = i;
}
通过消除整除部分,可以在一次操作后直接减小剩余值 n,而不需要进行迭代
//时间复杂度较大
while (1)
{
i = llabs(n - k);
if (i >= n)
{
break;
}
n = i;
}
T18 计算最长子串
int longestACGTSubstring(char *s)
{
int maxLength = 0;
int currentLength = 0;
while (*s)
{
if (*s == 'A' || *s == 'C' || *s == 'G' || *s == 'T')
{
currentLength++;
}
else
{
if (currentLength > maxLength)
{
maxLength = currentLength;
}
currentLength = 0;
}
s++;
}
if (currentLength > maxLength)
{
maxLength = currentLength;
}
return maxLength;
}
T25 康拓展开
我们有两个大小为N的排列P和Q(即,P和Q都是(1,2,…,N)的重新排列)
大小为N的排列共有N!种可能。其中,设P和Q分别为按字典顺序排列的第a个和第b个排列。要找到|a-b|
//阶乘在这里是为了确定每个数字在康托展开中的权重,表示在这个数字之后的所有可能的排列数量。
long long factorial(int n)
{
if (n <= 1)
return 1;
return n * factorial(n - 1);
}
int cantor(int a[], int n)
{
int i, j, x = 0;
for (i = 0; i < n; ++i)
{
int rank = 0;
for (j = i + 1; j < n; ++j)
{
if (a[j] < a[i])
rank++;
}
x += rank * factorial(n - i - 1);
}
return x;
}
康托展开:
求解一个排列的序号,比如:12345 序号为1,12354序号为2,按字典序增加编号递增,依次类推。
在上面给出的代码中,阶乘的主要作用是用于计算康托展开。
当我们计算一个排列的康托展开值时,每一位上的数字对应的值取决于该数字之后所有比它小的数字的数量,乘以对应位置的阶乘。
例如,考虑一个简单的排列 3 1 2:
1. 对于第一个数字3,后面比它小的数字有2个(即1和2),所以它的值是 2 × (3-1)! = 2 × 2! = 4。
2. 对于第二个数字1,后面没有比它小的数字,所以它的值是 0 × (3-2)! = 0 × 1! = 0。
3. 对于第三个数字2,它后面没有数字,所以它的值总是0。
排列的康托展开值是这些数字的和:4 + 0 + 0 = 4。
康托逆展开: 求解一个序号它对应的排列是什么。
Eg:如果初始序列是12345(第一个),让你求第107个序列是什么。(按字典序递增)
这样计算:
先把107减1,因为康托展开里的初始序列编号为0
然后计算下后缀积:
1 2 3 4 5
5! 4! 3! 2!1! 0!
120 24 6 2 1 1
106 / 4! = 4 ······ 10 有4个比它小的所以因该是5 从(1,2,3,4,5)里选
10 / 3! = 1 ······ 4 有1个比它小的所以因该是2 从(1, 2, 3, 4)里选
4 / 2! = 2 ······ 0 有2个比它小的所以因该是4 从(1, 3, 4)里选
0 / 1! = 0 ······ 0 有0个比它小的所以因该是1 从(1,3)里选
0 / 0! = 0 ······ 0 有0个比它小的所以因该是3 从(3)里选
所以编号107的是 52413
void calculate_factorials(int n)
{
int i;
factorial[0] = factorial[1] = 1;
for (i = 2; i <= n; i++)
{
factorial[i] = factorial[i - 1] * i;
}
}
void reverse_cantor(int k) {
vector<char> characters;
for (int i = 1; i <= num; i++) {
characters.push_back(i + '0');
}
string ans = "";
k--; // Adjustment for 0-based indexing
for (int i = 1; i <= num; i++)
{
int t = k / factorial[num - i];
k %= factorial[num - i];
ans += characters[t];
characters.erase(characters.begin() + t);
}
cout << ans << '\n';
}
T26 红球和篮球排成排
Takahashi一开始有很多红球和蓝球。现在,他将把它们排成一行。
起初,没有球被放置。
Takahashi非常耐心,他会执行以下操作10的100次方次:
在已经放置的球的末尾放置A个蓝球。然后,在末尾放置B个红球。
在这样制作的球行中,前N个球中有多少个蓝球?
//注意分的情况,diy的时候分的情况过于杂乱
int main()
{
long long int n, a, b;
scanf("%lld %lld %lld", &n, &a, &b);
long long int num;
if (n <= a)
{
num = n;
}
else
{
// num = a * (n / (a + b)) + (n % (a + b) - a);
num = a * (n / (a + b)) + ((n % (a + b) >= a) ? a : (n % (a + b)));
}
}
n种钱加起来等于某固定值
T34选取一些硬币,使得它们的总和为 X 日元
你有以下硬币:
A 枚面值为 500 日元的硬币,
B 枚面值为 100 日元的硬币,
C 枚面值为 50 日元的硬币(日元是日本的货币)。我们要选取一些硬币,使得它们的总和为 X 日元。
int main()
{
int a, b, c, i, j, k, x, count = 0;
scanf("%d%d%d%d", &a, &b, &c, &x);
int dp[a + 1][b + 1][c + 1];
dp[0][0][0] = 1;
for (i = 0; i < a + 1; i++)
{
for (j = 0; j < b + 1; i++)
{
for (k = 0; k < c + 1; i++)
{
dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - 1][k] + dp[i][j][k - 1];
}
}
}
printf("%d", dp[i][j][k]);
}
T70动态规划求解
AtCoder Mart每个商品都有1000000个:
饭团,每个售价100日元
三明治,每个售价101日元
饼干,每个售价102日元
蛋糕,每个售价103日元
糖果,每个售价104日元
电脑,每个售价105日元
Takahashi想购买一些商品,总价格恰好为X日元。您需要确定是否可能以给定的总金额购买这些物品。
#define MAX 1000000
int main()
{
int i, j, x;
int prices[] = {100, 101, 102, 103, 104, 105};
int counts[] = {MAX, MAX, MAX, MAX, MAX, MAX};
scanf("%d", &x);
int dp[MAX + 1] = {0};
dp[0] = 1;
for (i = 0; i < 6; i++)// 外层循环遍历零钱的面值(共6个面值)
{
for (j = prices[i]; j <= x; j++)// 从当前零钱的面值开始遍历
{
// dp[j - prices[i]]表示使用当前面值的零钱兑换剩余金额j - prices[i]所需要的最少硬币数量。如果dp[j - prices[i]]为非零值,说明可以使用当前面值的零钱兑换剩余金额,也就是说存在一种找零方案。
//dp[j - prices[i]]必须不为零,即使用当前面值的零钱兑换剩余金额是可行的,并且dp[j - prices[i]]必须小于counts[i],counts[i]是限制当前面值的最大使用数量。
if (dp[j - prices[i]] && dp[j - prices[i]] < counts[i])
{
dp[j] = dp[j - prices[i]] + 1;// 使用当前面值的零钱进行兑换所需的最少数量。这里加1表示使用了一个当前面值的零钱
}
}
}
if (dp[x])
printf("1\n");
else
printf("0\n");
}
T82依次找到最小值
你来到了你最喜欢的商店Infinitesco,打算买一些冰茶。
该商店以不同容量的瓶子出售冰茶,每种容量对应不同的价格。具体而言,0.25升的瓶子售价为Q日元,0.5升的瓶子售价为H日元,1升的瓶子售价为S日元,2升的瓶子售价为D日元。商店拥有无限数量的每种容量的瓶子。
你想要购买恰好N升的冰茶。那么你需要花费多少日元?
int main()
{
long long int q, h, s, d, n, min;
scanf("%lld %lld %lld %lld", &q, &h, &s, &d);
scanf("%lld", &n);
min = 4 * q;
if (min > 2 * h)
min = 2 * h;
if (min > s)
min = s;
if (min * 2 < d)
printf("%lld\n", min * n);
else
printf("%lld\n", (n % 2) * min + (n / 2) * d);
}
T35 转换思维角度
给定一个由小写英文字母组成的字符串S,找出在S中没有出现的字母中最小的字母(按字典顺序排列)。
如果S中的每个小写英文字母都出现了,则输出None。
S由小写英文字母组成。
// 从被调查的字母的范围入手
int function(char alpha, char *s)
{
int i;
int len = strlen(s);
for (i = 0; i < len; i++)
{
if (alpha == s[i])
{
return 0;
}
}
return 1;
}
int main()
{
char s[1000000];
char alphabet[26] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
scanf("%s", s);
int i;
for (i = 0; i < 26; i++)
{
if (function(alphabet[i], s))
{
printf("%c\n", alphabet[i]);
return 0;
}
}
printf("None\n");
}
T36 巧妙的思路,题目拗口
给定 N 张身份证和 M 个闸门,每张身份证允许通过特定的一系列闸门。问题要求计算能够独自通过所有闸门的身份证数量。
#define INF 1000000000
int main()
{
int N, M, L, R;
scanf("%d %d", &N, &M);
int left, right; // 记录所有闸门共同的有效范围。
left = 0;
right = INF;
for (int i = 1; i <= M; i++)
{
scanf("%d %d", &L, &R);
if (L > left)
left = L;
if (R < right)
right = R;
}
if (right >= left) // 检查是否存在一个有效的身份证范围,right 必须大于等于 left,才有可能有通过的身份证。
printf("%d\n", right - left + 1);
else
printf("0\n");
return 0;
}
T37 快手竞猜游戏–时间复杂度
高桥决定举办最快手竞猜游戏。负责制作记分板的木桥正在努力编写一个管理玩家得分的程序,游戏过程如下:
一共有N名玩家,编号从1到N。在游戏开始时,每个玩家初始得分为K。
当一个玩家回答问题正确时,其他N-1名玩家的得分各减少1分。没有其他因素会影响玩家的得分。
在游戏结束时,得分为0或低于0的玩家将被淘汰,剩下的玩家生存。
在最后一场游戏中,玩家们总共给出了Q个正确答案,其中第i个由玩家Ai给出。
请为木桥编写一个程序,确定每个玩家是否在这场游戏中幸存下来。
// 慎用! 二重循环!
int main()
{
long long int n, k, q;
scanf("%lld %lld %lld", &n, &k, &q);
int i, j;
long long int a[q], fraction[n];
for (i = 0; i < n; i++)
fraction[i] = k;
for (i = 0; i < q; i++)
{
scanf("%lld", &a[i]);
a[i]--;
}
for (i = 0; i < q; i++)
{
for (j = 0; j < n; j++)
{
if (j != a[i])
{
fraction[j]--; // 如果不是第a[i]位同学,分数-1
}
}
}
for (i = 0; i < n; i++)
{
if (fraction[i] <= 0)
{
printf("No\n");
}
else
printf("Yes\n");
}
}
//时间较少,只有一重循环
#include <stdio.h>
int main()
{
long long int n, k, q;
scanf("%lld %lld %lld", &n, &k, &q);
int i;
long long int a[q], fraction[n];
for (i = 0; i < n; i++)
fraction[i] = 0;
for (i = 0; i < q; i++)
{
scanf("%lld", &a[i]);
a[i]--;
fraction[a[i]]++; // 统计每个回答被选择的次数
}
for (i = 0; i < n; i++)
{
long long int final_score = k - (q - fraction[i]); // 关键一步.计算每个玩家最终的分数
if (final_score <= 0)
printf("No\n");
else
printf("Yes\n");
}
}
T41 统计Pi≤其前面位置的所有元素的个数
慎用二重循环!!
题目要求统计有多少个位置i,使得在排列中,位置i上的元素Pi都小于等于其前面位置的所有元素。
int main()
{
long long int n, count = 1;
int i, a[n];
scanf("%lld", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
int min_so_far = a[0]; // 记录迄今为止的最小值
for (i = 1; i < n; i++)
{
if (a[i] <= min_so_far)
{ // 如果当前元素小于等于迄今为止的最小值
min_so_far = a[i];
count++;
}
}
printf("%lld", count);
}
getchar()问题
输入字符和数组时遇到换行符 需要getchar()
scanf函数和getchar()函数读取数据都实在键盘的缓冲区中,当scanf内容输入完毕时会在键盘按下回车即\n,getchar会在键盘缓冲区读取\n,从而导致程序提前结束。
以下两道题注意输入的时候的getchar
图片的外围绘制这个边框
题目:在给定的高度为 H 像素、宽度为 W 像素的图片周围绘制一个由符号 # 组成的边框,边框的厚度为 1 像素。图片中的每个像素由小写英文字母表示。你需要在图片的外围绘制这个边框,然后输出结果。边框应该紧密地包围整个图片。
int main()
{
int h, w, i, j;
scanf("%d %d", &h, &w);
char a[100][100];
getchar();
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
scanf("%c", &a[i][j]);
}
getchar();
}
}
一行复制到新图像的两行中,生成一个高度加倍的图像
题目:将原图像中的每一行复制到新图像的两行中,生成一个高度加倍的图像
int main()
{
int h, w, i, j;
scanf("%d %d", &h, &w);
char c[h][w];
for (i = 0; i < h; i++)
{
getchar();
for (j = 0; j < w; j++)
{
scanf("%c", &c[i][j]);
}
}
}
判断是否为正确的密码
题目:输入一个字符串,自己判断是否为正确的密码,如果正确输入 Y 否则输入 N ,在进行字符判断
为 Y 输出“确认成功”否则输出“确认失败”。
int main()
{
char s[20];
scanf("%s", s);
getchar();
char a = getchar();
if (a == 'Y' || a == 'y')
{
printf("YES!!\n");
}
else
printf("NO!!\n");
}
当我们输入密码为123456 789时(中间有一个空格),还没等我们输入N或Y,又立马跳出了“确认失败”。
getchar()取走了缓冲区中的7,根本不需要从键盘输入N或Y。
运行时自然它就自动跳出了“NO”。
我们需要把缓冲区中的东西先全部清走。
用一个循环,只要没读到\n,我们就一直用getchar()读。
//把缓冲区中的内容全读走,空格后面的都无法存储在字符串中
while ( getchar() != '\n')
{
;
}
T57 税率和消费税金额
一个商品在税率分别为 8% 和 10% 时,对应的消费税金额分别为 A 日元和 B 日元的价格。
这里,税前价格必须是正整数,消费税金额向下取整到最接近的整数。
如果有多个价格满足条件,则打印最低的价格;如果没有价格满足条件,则打印 -1。(1 <= a <= b <= 100)
#include <stdio.h>
int main()
{
int a, b;
scanf("%d %d", &a, &b);
int x = 1;
while ((int)(x * 0.08) < a)
x++;
while ((int)(x * 0.1) < b)
x++;
if ((int)(x * 0.08) == a && (int)(x * 0.1) == b) //绝妙一步
printf("%d\n", x);
else
printf("-1\n");
}
设置每天的旅行距离,第N天返回
Snuke生活在一个无限的二维平面上。他将进行为期N天的旅行。在第1天的开始,他在家里。他的计划由长度为N的字符串S描述。在第i天(1≦i≦N),他将按以下方向前进一个正距离:(1≦∣S∣≦1000)
如果S的第i个字母是N,则向北前进 类推
他还没有确定每天的旅行距离。确定是否可能设置每天的旅行距离,使得他在第N天结束时回到家中。
统计坐标的位置,坐标在原点 或者最后一天日行八万里(另一个坐标为0)
#include <stdio.h>
#include <stdbool.h>
int main()
{
// 检查输入字符串中是否包含相等数量的北方和南方方向
bool n = false, w = false, s = false, e = false;
char c;
while (scanf("%c", &c) != EOF)
{
if (c == 'N')
n = true;
else if (c == 'W')
w = true;
else if (c == 'S')
s = true;
else if (c == 'E')
e = true;
}
printf((n == s) && (w == e) ? "Yes" : "No");
return 0;
}
字典排序 – n个字符串的qsort
基础思路: 冒泡排序法逐个比较并且交换字符串
int main()
{
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
{
if (strcmp(a[i], a[j]) > 0)
{ // 如果前一个字符的ASCII码大于后一个则交换位置
// 这个则由strcmp的值来反应,如果前一个元素大于后一个元素
// 则返回值1,如果相等返回值0,如果小于,返回值-1
strcpy(b, a[i]); // 利用临时数组存放字符串
strcpy(a[i], a[j]); // 将括号里的第二个元素赋给第一个元素
strcpy(a[j], b); // 结果是后一个与前一个交换
}
}
}
出现次数最多的字符串
我们有N张选票。第i张选票(1≤i≤N)上写有字符串Si。
按字典顺序打印出所有出现次数最多的字符串。
#define MAX_LENGTH 10
int cnt[200001];
int cmp(const void *a, const void *b)
{
return strcmp(*(char **)a, *(char **)b);
}
int main()
{
int n, i, max = 0, len;
scanf("%d", &n);
char **s = (char **)malloc(n * sizeof(char *));
getchar();
for (i = 0; i < n; i++)
{
s[i] = (char *)malloc((MAX_LENGTH + 1) * sizeof(char));
scanf("%s", s[i]);
getchar();
}
qsort(s, n, sizeof(char *), cmp);
cnt[0] = 1;
for (i = 1; i < n; i++)
{
if (strcmp(s[i - 1], s[i]) == 0)
cnt[i] = cnt[i - 1] + 1;
else
{
cnt[i] = 1;
if (cnt[i - 1] > max)
max = cnt[i - 1];
}
}
if (cnt[n - 1] > max)
max = cnt[n - 1];
for (i = 0; i < n; i++)
{
if (cnt[i] == max)
printf("%s\n", s[i]);
}
free(s);
return 0;
}
// 一个qsort搞定
typedef struct
{
char city[50];
int rating;
int id;
} Res;
int compare(const void *a, const void *b)
{
Res *resA = (Res *)a;
Res *resB = (Res *)b;
// 先按城市名称进行比较
int cityComparison = strcmp(resA->city, resB->city);
if (cityComparison != 0)
{
return cityComparison;
}
// 如果同一城市,按评分降序排列
return resB->rating - resA->rating;
}
int main()
{
for (i = 0; i < N; i++)
{
scanf("%s%d", res[i].city, &res[i].rating);
res[i].id = i + 1;
}
qsort(res, N, sizeof(Res), compare);
}
给定一个由N个长度为L的字符串组成的序列,要求找到这些字符串按照某种顺序连接起来后,字典序最小的长字符串。
int compare(const void *a, const void *b)
{
const char *str1 = (const char *)a;
const char *str2 = (const char *)b;
return strcmp(str1, str2);
}
int main()
{
int n, i, l;
scanf("%d %d", &n, &l);
char s[n][l + 1]; // l + 1很重要
for (i = 0; i < n; i++)
scanf("%s", s[i]);
qsort(s, n, sizeof(char) * (l + 1), compare);
char result[l * n];
strcpy(result, s[0]);
for (i = 1; i < n; i++)
strcat(result, s[i]);
printf("%s\n", result);
}
字符串相互比较出相同的最大数时,直接开始一个一个比,不用担心重复,毕竟也改编不了max的值
蓝色卡片红色卡片
Takahashi有N张蓝色卡片和M张红色卡片。每张卡片上都写着一个字符串。第i张蓝色卡片上写着si,而第i张红色卡片上写着ti。
现在,Takahashi将宣布一个字符串,并检查每张卡片。每当他找到一张与他宣布的字符串相同的蓝色卡片时,他将赚取1日元(日本的货币);每当他找到一张相同字符串的红色卡片时,他将损失1日元。
这里,我们只考虑Takahashi宣布的字符串与卡片上的字符串完全相同的情况。例如,如果他宣布"atcoder",即使有蓝色卡片上写着"atcoderr"、“atcode”、"btcoder"等,他也不会赚钱(相应地,如果有红色卡片上有这样的字符串,他也不会失去钱)。
最多可以赚到多少日元?请注意,同样的字符串可能写在多张卡片上。
int main()
{
int n, m, i, j;
scanf("%d", &n);
int max = 0;
char s[101][11], t[101][11];
getchar();
for (i = 0; i < n; i++)
scanf("%s", s[i]);
scanf("%d", &m);
getchar();
for (i = 0; i < m; i++)
scanf("%s", t[i]);
int count1, count2;
for (i = 0; i < n; i++)
{
count1 = 0;
count2 = 0;
for (j = i; j < n; j++)
if (strcmp(s[i], s[j]) == 0)
count1++;
for (j = 0; j < m; j++)
if (strcmp(s[i], t[j]) == 0)
count2++;
if (count1 - count2 > max)
max = count1 - count2;
}
printf("%d\n", max);
return 0;
}
计算字符串中最少需要添加的字符数量,使得相邻字符不相同的问题。(题目就没懂)
给定一个由小写英文字母组成的字符串 S,要求找到最大的正整数 K 满足以下条件:
存在一种将 S 分割成 K 个非空字符串 S₁, S₂, …, SK 的方法,其中对于所有的 1 ≤ i ≤ K-1,有 Si ≠ Si₊₁。
简单解释一下题目的意思:要找到一个最大的整数 K,使得字符串 S 可以被分割为 K 个非空的子串,并且相邻的子串不能相同。
char str[200001];
int main()
{
scanf("%s", str);
int ans = 1, len = strlen(str);
int t = 0, i, x;
for (i = 0; i < len - 1;)
{
x = 0;
if (t == 0)
{
if (str[i] != str[i + 1]) // 当前字符与下一个字符不相同
ans++, i++;
if (str[i] == str[i + 1] && i + 1 != len - 1) // 当前字符与下一个字符相同且下下个字符不是字符串的最后一个字符
ans++, i += 2, x = 1, t = 1;
if (str[i] == str[i + 1] && i + 1 == len - 1 && x == 0) // 当前字符与下一个字符相同且下下个字符是字符串的最后一个字符且x为0(表示特殊情况未发生,)跳出循环
break;
}
if (x == 1) // 表示特殊情况发生
continue;
if (t == 1) //表示已重复
{
ans++, t = 0, i++;
}
}
printf("%d\n", ans);
return 0;
}
2个半份合成1份
在快餐连锁店"Pizza At"中,提供三种披萨:“A-pizza”、“B-pizza"和"AB-pizza”。A-pizza和B-pizza是完全不同的披萨,而AB-pizza则是将A-pizza和B-pizza的一半组合在一起。其中,一份A-pizza的价格为A日元,一份B-pizza的价格为B日元,一份AB-pizza的价格为C日元(日本货币)。
Nakahashi今晚需要准备X份A-pizza和Y份B-pizza。他可以通过直接购买A-pizza和B-pizza,或者购买两份AB-pizza然后重新组合成一份A-pizza和一份B-pizza来获得这些披萨。最少需要多少钱呢?由于可以通过重新组合披萨来多出一些披萨,所以允许数量上有所超出。
2种思路
int main()
{
long long A, B, C, X, Y;
scanf("%lld %lld %lld %lld %lld", &A, &B, &C, &X, &Y);
int ab;
long long ans = 1LL << 60; // 在二进制表示中,它将在最左边添加 60 个零,形成一个非常大的数值。
for (ab = 0; ab < 201010; ab++)
{
long long sm = C * ab;
long long x = X - ab / 2;
long long y = Y - ab / 2;
if (0 < x)
sm += x * A;
if (0 < y)
sm += y * B;
ans = (ans < sm) ? ans : sm;
}
printf("%lld\n", ans);
}
int main()
{
int pra, prb, prc;
int x, y;
long long int ans = 0;
while (scanf("%d %d %d %d %d", &pra, &prb, &prc, &x, &y) == 5)
{
ans = 0;
if (prc * 2 < pra + prb)
{
ans += (x < y ? x : y) * prc * 2;
if (x > y)
ans += (x - y) * pra;
else
ans += (y - x) * prb;
}
else
ans = x * pra + y * prb;
if (prc * 2 * (x > y ? x : y) < ans)
ans = prc * 2 * (x > y ? x : y);
printf("%lld\n", ans);
}
}
罚时数量计算
Takahashi参加了AtCoder的一场比赛。
比赛中共有N道题目。
Takahashi在比赛中进行了M次提交。
第i次提交是针对第pi个问题,并得到了评判结果Si(AC或WA)。
Takahashi的正确答案数量是他至少收到一个AC评判的问题数量。
Takahashi的罚时是他在至少收到一个AC评判的问题上的以下计数之和:在第一次收到AC之前收到的WA数量。
请找出Takahashi的正确答案数量和罚时数量。
//diy
int count[100001] = {0}, p[100001] = {0}, num[100001] = {0};
int main()
{
int n, m, i, j;
char s[100000][3]; // 应该是3
// 主要区别在于字符数组的大小。在你的代码中,字符数组 s[100000][2] 的第二个维度大小为2,这意味着每个字符串只能存储一个字符和一个空字符(用于字符串结束)。这可能会导致输入的字符串被截断,只保留第一个字符。
// 在我给出的代码中,我将第二个维度的大小更改为101,允许每个字符串存储最多100个字符(加上一个空字符)。这样可以确保输入的字符串被完整地读入和存储,从而可以原封不动地输出。
// 如果你的输入字符串确实只有一个字符,那么你的代码将工作正常。但是,如果输入的字符串可能包含多个字符,你的代码将无法正确地存储和输出完整的字符串。在这种情况下,你应该考虑增加第二个维度的大小,以便能够存储更长的字符串。
scanf("%d %d", &n, &m);
long long int ans = 0, rightnum = 0;
getchar();
for (i = 0; i < m; i++)
{
scanf("%d %s", &p[i], s[i]);
num[p[i] - 1]++;
if (strcmp(s[i], "AC") == 0)
{
count[p[i] - 1] = 1;
}
getchar();
}
for (i = 0; i < n; i++)
{
if (count[i] == 1)
{
rightnum++;
}
}
for (i = 0; i < m; i++)
{
if (i == m - 1)
break;
int temp = p[i];
if (strcmp(s[i], "AC") == 0)
{
while (1)
{
if (p[i] == temp)
{
i++;
if (i == m - 1)
{
printf("%lld %lld\n", rightnum, ans);
return 0;
}
}
else
break;
}
}
if (strcmp(s[i], "AC") == 0)
{
continue;
}
ans++;
}
printf("%lld %lld\n", rightnum, ans);
}
//ac
int main()
{
int n, m, i,p;
scanf("%d %d", &n, &m);
int ans[n][2];
memset(ans, 0, sizeof(ans)); // 将数组初始化为0
char s[3];
for (i = 0; i < m; i++)
{
scanf("%d %s", &p, s);
p--;
if ('A' == s[0]) // 根据评判结果的首字母
ans[p][0] = 1; // 表示第p个问题是否收到了AC评判
else if (!ans[p][0]) // 问题的正确答案数量为0,ans[p][0]的值为0(即未收到AC评判),那么取反后的值为真
ans[p][1]++; // 罚时数量加一
}
int ac = 0, pena = 0;
for (i = 0; i < n; i++)
{
ac += ans[i][0];
if (ans[i][0])
pena += ans[i][1];
}
printf("%d %d\n", ac, pena);
}
当考虑的情况越想越复杂时,不如从出发点想想
–时,最后一位是最大的,所以要从数组最后开始往前循环
当考虑的情况越想越复杂时,不如从出发点想想
如果从最小的开始出发,需要考虑的特殊情况会越来越多,所以需要更改出发点
题目:从左到右排列了N个方块,第i个方块的高度为H i。
对于每个方块,你可以选择执行以下操作之一:
减小方块的高度1个单位。
不进行任何操作。
判断是否可以通过执行操作使得方块的高度从左到右保持非递减。
int main()
{
int n, i, a[100000];
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i = n - 1; i > 0; i--)
{
if (a[i - 1] - 1 > a[i])
{
printf("No\n");
return 0;
}
if (a[i - 1] - 1 == a[i])
{
a[i - 1] -= 1;
}
}
printf("Yes\n");
return 0;
}
2都替换为22,每个3都替换为333
有一个由1到9的数字组成的字符串S。
每次日期变化时,该字符串的变化如下:
S中的每个2都替换为22,每个3都替换为333,每个4都替换为4444,每个5都替换为55555,每个6都替换为666666,每个7都替换为7777777,每个8都替换为88888888,每个9都替换为999999999。1保持不变。
经过5×10^15天后,字符串的第K个字符是什么。
char s[101];
long long k;
int main()
{
scanf("%s", s);
scanf("%lld", &k);
int t = 1;
for (int i = 0; i < k; i++)
{
if (s[i] != '1')
{
printf("%c\n", s[i]);
t = 0;
break;
}
}
if (t == 1)
printf("1\n");
return 0;
}
数字很大时,要另辟蹊径,从数学角度找规律(T87https)
你给出了N个正整数a1, a2, …, aN。
对于一个非负整数m,定义f(m) = (m mod a1) + (m mod a2) + … + (m mod aN)。
这里,X mod Y 表示X除以Y的余数。
int main()
{
int n;
scanf("%d", &n);
int ryoka;
int i, ans = -n;
for (i = 0; i < n; i++)
{
scanf("%d", &ryoka);
ans += ryoka;
}
printf("%d\n", ans);
return 0;
}
走遍城镇的平均长度
在二维坐标平面上有 N 个城镇。第 i 个城镇位于坐标 (xi, yi) 的位置。城镇 i 和城镇 j 之间的距离为 (xi - xj)^2 + (yi - yj)^2。
总共有 N! 种可能的路径可以依次访问所有这些城镇。路径的长度定义为从路径的第一个城镇开始,依次访问第二个、第三个…一直到最后一个城镇时所经过的距离(假设我们会直线行走)。请计算这 N! 条路径的平均长度。
int main()
{
int n, i, j;
int x[8], y[8];
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d%d", &x[i], &y[i]);
double s = 0;
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
s += sqrt(pow(x[i] - x[j], 2) + pow(y[i] - y[j], 2));
printf("%.7f\n", s / n * 2);
}
数字的角度
输出未知长度的数组 - 用字符串代替,然后直接strlen(https)
题目:桌子上有 N 个立方体按垂直方向堆叠。
给定长度为 N 的字符串 S。如果 S 中第 i 个字符是 0,那么从底部起的第 i 个立方体的颜色是红色,如果该字符是 1,则是蓝色。
您可以任意次地执行以下操作:选择一个相邻的红色立方体和蓝色立方体,并将它们移除。这里,被移除的立方体上堆叠的立方体将掉落到下面的物体上。
最多可以移除多少个立方体?
直接从数字的角度,不用管考虑方块从上掉落的循环过程
符号ask码的循环https
输入以0开头的数字或者在一行内输入不带空格的4位数字
printf("%04d", num); //表示前面不够4位数字的用0补充
数组先初始化为0,然后一一填入
//符号ask码的循环
int main()
{
int i = 3, temp, diff[4] = {0}, A, B, C, D;
scanf("%d", &temp);
while (temp)
{
diff[i] = temp % 10;
temp /= 10;
i--;
}
int result = 0;
A = diff[0];B = diff[1];C = diff[2];D = diff[3];
for (char op1 = '+'; op1 <= '-'; op1++)
{
if (op1 != ',')
{
for (char op2 = '+'; op2 <= '-'; op2++)
{
if (op2 != ',')
{
for (char op3 = '+'; op3 <= '-'; op3++)
{
if (op3 != ',')
{
result = (op1 == '+') ? (A + B) : (A - B);
result = (op2 == '+') ? (result + C) : (result - C);
result = (op3 == '+') ? (result + D) : (result - D);
if (result == 7)
{
printf("%d%c%d%c%d%c%d=%d\n", A, op1, B, op2, C, op3, D, result);
return 0;
}
}
}
}
}
}
}
}
数组的每个元素都递归
我们说长度为 N 的两个整数序列 x₁, x₂, …, xₙ 和 y₁, y₂, …, yₙ 是相似的,当对于所有 i (1 ≤ i ≤ N) 都满足 ∣xᵢ - yᵢ∣ ≤ 1 时。
特别地,任何整数序列与自身是相似的。
给定一个整数 N 和一个长度为 N 的整数序列 A₁, A₂, …, Aₙ,有多少个整数序列 b₁, b₂, …, bₙ 存在,满足 b₁, b₂, …, bₙ 与 A 相似,并且所有元素的乘积 b₁b₂…bₙ 是偶数的?
int count = 0;
void generate_sequences(int *A, int *B, int pos, int N)
{
int i, num;
// 如果我们到达序列的末尾,检查乘积是否为偶数,然后计数
if (pos == N)
{
int product = 1;
for (i = 0; i < N; i++)
product *= B[i];
if (product % 2 == 0)
count++;
return;
}
// 考虑当前位置的数字
for (num = A[pos] - 1; num <= A[pos] + 1; num++)
{
// 检查相似性条件
if (abs(num - A[pos]) <= 1)
{
// 递归生成下一个位置的数字序列
B[pos] = num;
generate_sequences(A, B, pos + 1, N);
}
}
}
int main()
{
int n, i;
scanf("%d", &n);
int a[n];
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
int *b = (int *)malloc(n * sizeof(int));
generate_sequences(a, b, 0, n);
printf("%d\n", count);
free(a);
free(b);
}
第 i 只兔子喜欢兔子a[i]
有 N 只兔子,编号从 1 到 N。
第 i 只(1≤i≤N)兔子喜欢兔子 a_i。注意,每只兔子不能喜欢自己,即 a[i] ≠ i。
对于兔子 i 和 j(i<j),如果满足以下条件,我们称其为友好对:
兔子 i 喜欢兔子 j,兔子 j 喜欢兔子 i。 计算友好对的数量。
//更新前
int main()
{
int n, i, j, a[100000];
long long int ans = 0;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
for (i = 0; i < n; i++)
{
for (j = 0 ; j < n; j++)
{
if (a[i] == j + 1 && a[j] == i + 1 )
ans++;
}
}
printf("%lld\n", ans/2);
}
//更新后
int main()
{
int N, i, count = 0;
scanf("%d", &N);
int rabbits[N + 1];
for (i = 1; i <= N; i++)
scanf("%d", &rabbits[i]);
for (i = 1; i <= N; i++)
{
if (rabbits[rabbits[i]] == i)
count++;
}
printf("%d\n", count/2);
return 0;
}
前130道题的总结到此结束,8.22完
1.黑白翻转棋子对–寻找连续两项不同
思路
检查当前位置i的棋子颜色是否为黑色(即不为’W’)。
如果是黑色,将count++,表示找到了一对相邻棋子颜色不同。
否则,说明遇到了一个白色的棋子,此时将count累加到sum上,
表示已经找到了一组可以进行翻转操作的棋子对。
char s[2000001];
int main()
{
int i = 0, n;
long long int count = 0, sum = 0;
scanf("%s", s);
n = strlen(s);
while (i < n)
{
if (s[i] == 'B')
count++;
else
sum += count;
i++;
printf("i: %d count:%lld\n", i, sum);
}
printf("%lld\n", sum);
}
2.偶串
题目
我们称由两个相同字符串连接而成的字符串为偶串。
例如,xyzxyz和aaaaaa都是偶串,而ababab和xyzxy不是。
给定一个由小写英文字母组成的偶串S。
找出通过删除S末尾的一个或多个字符可以得到的最长偶串的长度。
保证对于给定的输入,一定存在这样一个非空字符串。
思路
从后往前遍历,i每次减2,判断剩下的字符串的各一半是否相等,count++,
如果count== 字符串一般长的话,说明符合题意了
int main()
{
char s[201];
scanf("%s", s);
int n = strlen(s), i, j, k, count;
for (i = n - 3; i >= 0; i -= 2)
{
count = 0;
for (j = 0; j < (i + 1) / 2; j++)
{
if (s[j] == s[j + (i + 1) / 2])
{
count++;
}
}
if (count == (i + 1) / 2)
{
printf("%d\n", i + 1);
break;
}
}
}
3.余数找规律
题目
给定一个整数 N,我们需要从数字集合 {1, 2, …, N} 中选择一个排列 {P1, P2, …, PN}。
对于每个 i = 1, 2, …, N,令 Mi 为 i 除以 Pi 所得的余数。
任务是找到能够使 M1 + M2 + … + MN 取得最大值的排列方式。
思路
寻找规律,结果是n*(n-1)/2
1. 首先,考虑最简单的情况,即当N=2时。我们从数字集合{1, 2}中选择一个排列。根据题目描述,M1和M2的取值范围分别是{0, 1}。我们可以尝试不同的排列并计算M1 + M2的值:
2. 若选择排列{1, 2},则M1 = 1%1 = 0,M2 = 2%2 = 0,因此M1 + M2 = 0 + 0 = 0。
若选择排列{2, 1},则M1 = 1%2 = 1,M2 = 2%1 = 0,因此M1 + M2 = 1 + 0 = 1。
根据上述计算结果,我们发现在N=2时,M1 + M2 的最大值为1。可以进一步验证,无论如何排列数字,得到的结果都不会超过1。
3. 然后,我们再考虑N=3的情况。我们从数字集合{1, 2, 3}中选择一个排列。根据题目描述,M1、M2和M3的取值范围分别是{0, 1, 2}。我们可以尝试不同的排列并计算M1 + M2 + M3的值:
4. 若选择排列{1, 2, 3},则M1 = 1%1 = 0,M2 = 2%2 = 0,M3 = 3%3 = 0,因此M1 + M2 + M3 = 0 + 0 + 0 = 0。
若选择排列{1, 3, 2},则M1 = 1%1 = 0,M2 = 2%3 = 2,M3 = 3%2 = 1,因此M1 + M2 + M3 = 0 + 2 + 1 = 3。
若选择排列{2, 1, 3},则M1 = 1%2 = 1,M2 = 2%1 = 0,M3 = 3%3 = 0,因此M1 + M2 + M3 = 1 + 0 + 0 = 1。
若选择排列{2, 3, 1},则M1 = 1%2 = 1,M2 = 2%3 = 2,M3 = 3%1 = 0,因此M1 + M2 + M3 = 1 + 2 + 0 = 3。
若选择排列{3, 1, 2},则M1 = 1%3 = 1,M2 = 2%1 = 0,M3 = 3%2 = 1,因此M1 + M2 + M3 = 1 + 0 + 1 = 2。
若选择排列{3, 2, 1},则M1 = 1%3 = 1,M2 = 2%2 = 0,M3 = 3%1 = 0,因此M1 + M2 + M3 = 1 + 0 + 0 = 1。
5. 根据上述计算结果,我们发现在N=3时,M1 + M2 + M3 的最大值为3。可以进一步验证,无论如何排列数字,得到的结果都不会超过3。
6. 继续观察和推导其他N值的情况,我们可以发现规律:M1 + M2 + ... + MN 的最大值等于n*(n-1)/2。
7. 我们将此结论推广到一般情况。我们需要选择一个排列 {P1, P2, ..., PN}。
8. 对于 P1=1,M1 的取值为 0。
对于 P1=2,M1 的取值个数为 2-1=1。
对于 P1=3,M1 的取值个数为 3-1=2。
...
对于 P1=N,M1 的取值个数为 N-1。
我们可以发现,M1 的取值个数与 P1 的大小有关,且规律为 M1 的取值个数为 P1-1。由于排列是任意的,这个规律适用于所有的 M1, M2, ..., MN。
9. 综上所述,我们得出结论:M1 + M2 + ... + MN 的最大值为 (N-1) + (N-1) + ... + (N-1),共计 N-1 个 (N-1),即为 N*(N-1)/2。这是由于每个Mi的取值范围是{0, 1, ..., n-1},而它们的和正好是等差数列的前n项和公式。
long long int n;
int main()
{
scanf("%lld", &n);
printf("%lld\n", n * (n - 1) / 2);
return 0;
}
4.最优位置切割字符串–思路较难,代码简单
题目
给定一个由小写英文字母组成的长度为N的字符串S。
我们将在某个位置将此字符串切割为两个字符串X和Y。
在这里,我们希望最大化X和Y中包含的不同字母的数量。
找到在最优位置切割字符串时,X和Y中可能包含的最大不同字母的数量。
思路
切得位置从i=0开始,用两个循环比较前后两个子串中含有的公共字符
代码
代码很简单巧妙,但是完全看不懂,甚至不知道怎么提交通过的,还是用笨方法吧
c[2];
i;
m;
main(j, s) char *s;
{
gets(s);
for (gets(s); s[++i]; m = fmax(__builtin_popcount(*c & c[1]), m))
for (*c = c[1] = j = 0; s[j];)
c[j < i] |= 1 << s[j++] - 97;
printf("%d\n", m);
}
5.<>判断连续出现的次数
题目
给定一个长度为N-1的字符串S。S中的每个字符都是<或>。
一个由N个非负整数组成的序列a1, a2, …, aN被称为好序列,
当且仅当满足以下条件对于所有i (1 ≤ i ≤ N-1):
如果Si = <: ai < ai+1
如果Si = >: ai > ai+1
请找出可能的好序列的元素之和的最小值。
思路
另类连续相加
//特殊情况考虑少了,寄
int main()
{
char s[5000001];
scanf("%s", s);
int n = strlen(s), i, sum = 0, count = 0;
int a[n + 1];
for (i = 0; i < 1 + n; i++)
a[i] = 0;
for (i = 0; i < n; i++)
{
count = 0;
if (s[i] == s[i + 1])
count++;
else
sum += n * (n - 1) / 2;
}
printf("%d\n", sum);
return 0;
}
//利用空循环
int main()
{
char s[500001];
scanf("%s", s);
int i = 0;
long long int ans = 0;
for (; s[i] != '\0';)
{
long long int tmp1 = 0;
for (; s[i] == '<' && s[i] != '\0'; i++, tmp1++) // 统计连续出现的 < 的数量
{
}
ans += tmp1 * (tmp1 - 1) / 2; // 将 tmp1 个 < 转换为对应的好序列的元素之和并加入答案
long long int tmp2 = 0;
for (; s[i] == '>' && s[i] != '\0'; i++, tmp2++)
{
}
ans += tmp2 * (tmp2 - 1) / 2;
ans += (tmp1 > tmp2 ? tmp1 : tmp2);// 添加半个好序列,取 tmp1 和 tmp2 中较大的一个,半个好序列的定义是,其中的 < 和 > 的数量不一定相等,但我们要取其中数量较多的那一种字符来构建好序列。
}
printf("%lli", ans);
return 0;
}
6.欧几里得算法(计算gcd)
题目
要求您选择一些正整数,并计算它们的总和。
您可以选择任意多的整数,以及任意大的整数。
但是,您必须遵循以下规则:每个所选整数都必须是 A 的倍数,并且您至少需要选择一个整数。
您的目标是使所选整数的总和在模除 B 的情况下与 C 同余。请确定是否可能实现此目标。
如果可以实现,打印 “YES”。否则,打印 “NO”。
思路
若两个整数 A 和 B(A >= B)的最大公约数为 D,则 A 可以表示为 A = B * Q + R,其中 Q 是商,R 是余数。
根据这个性质,我们有 D 也是 B 和 R 的最大公约数。
连续地将较大数除以较小数取余数的方式,递归地求得最大公约数,直到余数变为零。
一旦余数变为零,此时较小的数就是最大公约数。
当余数为零时,返回另一个非零的数,这个数即为最大公约数,
每次都使用较小的数去除以较大的数取余数,直到余数为零。
int f(int a, int b)
{
if (b == 0)
return a;
return f(b, a % b);
}
int main()
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if (c % f(a, b) == 0) // 是否存在一个整数 D,使得 A 和 B 的最大公约数等于 D,并且 C 可以被 D 整除。
printf("YES\n");
else
printf("NO\n");
return 0;
}
7.数字作为数组的下标来表示出现的次数
题目
你被给定一个长度为N的整数序列,记作a₁, a₂, …, aₙ。
对于每个1 ≤ i ≤ N,你有三种选择:给aᵢ加1,从aᵢ减1,或者什么都不做。
在进行这些操作后,你要选择一个整数X,并统计满足aᵢ = X的i的数量。
通过做出最佳选择,最大化这个计数。
思路
将输入的数字作为下标,++这个下标前后对应的数组值,最后比大小
// 常规方法时间复杂度太大
int main()
{
int n;
scanf("%d", &n);
int a[n], i, j, ans = 0, count = 0;
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
{
count = 0;
for (j = 0; j < n; j++)
{
if (abs(a[j] - i) <= 1)
count++;
}
if (count > ans)
{
ans = count;
}
}
if (ans == 0)
{
ans = 1;
}
printf("%d\n", ans);
return 0;
}
# define INF 100001
int main()
{
int n, d;
scanf("%d", &n);
int a[INF], i, max = 0;
for (i = 0; i < INF; i++)
a[i] = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &d);
if (d == 0)
{
a[d]++;
}
else
{
a[d - 1]++;
a[d]++;
a[d + 1]++;
}
}
for (i = 0; i < INF; i++)
{
if (max < a[i])
{
max = a[i];
}
}
printf("%d\n", max);
}
8.不能用define的一道题
题目
有 N 名学生和 M 个检查点在二维平面上的坐标系中。
第 i 名学生(1 ≤ i ≤ N)的坐标是 (aᵢ, bᵢ),第 j 个检查点(1 ≤ j ≤ M)的坐标是 (cⱼ, dⱼ)。
当老师发出信号时,每名学生必须前往曼哈顿距离最近的检查点。
两点 (x₁, y₁) 和 (x₂, y₂) 之间的曼哈顿距离为 ∣x₁ - x₂∣ + ∣y₁ - y₂∣。
在这里,∣x∣ 表示 x 的绝对值。
如果对于一名学生有多个最近的检查点,他/她将选择索引最小的检查点。
每名学生将前往哪个检查点?
思路
错误的代码
1. 不能用difine在这道题
2. 超过10位数时再考虑用long long
#define INF pow(10, 8) + 1;
int main()
{
int n, m, i, j, index;
scanf("%d %d", &n, &m);
long long int a[n][2], b[m][2], min;
for (i = 0; i < n; i++)
scanf("%lld %lld", &a[i][0], &a[i][1]);
for (i = 0; i < m; i++)
scanf("%lld %lld", &b[i][0], &b[i][1]);
for (i = 0; i < n; i++)
{
min = INF;
index = 0;
for (j = 0; j < m; j++)
{
if (abs(a[i][0] - b[j][0]) + abs(a[i][1] - b[j][1]) < min)
{
min = abs(a[i][0] - b[j][0]) + abs(a[i][1] - b[j][1]);
index = j + 1;
}
}
printf("%d\n", index);
}
}
正确的代码
int main()
{
int n, m, i, j, index;
scanf("%d %d", &n, &m);
int a[n][2], b[m][2], min;
for (i = 0; i < n; i++)
scanf("%d %d", &a[i][0], &a[i][1]);
for (i = 0; i < m; i++)
scanf("%d %d", &b[i][0], &b[i][1]);
for (i = 0; i < n; i++)
{
min = 1000000000;
index = 0;
for (j = 0; j < m; j++)
{
if (abs(a[i][0] - b[j][0]) + abs(a[i][1] - b[j][1]) < min)
{
min = abs(a[i][0] - b[j][0]) + abs(a[i][1] - b[j][1]);
index = j + 1;
}
}
printf("%d\n", index);
}
}
9.二维数组作为函数参数
题目
有一个网格由H行W列的方块组成。第i行从上到下,第j列从左到右的方块表示为(i,j)。
每个方块可以是黑色或白色。
方块的颜色以一个H×W的矩阵表示(a[i][j])。
如果a[i][j]是".“,则方块(i,j)是白色;
如果a[i][j]是”#",则方块(i,j)是黑色。
Snuke正在对这个网格进行压缩。他将重复执行以下操作,直到没有只包含白色方块的行或列为止:
操作:选择任意一行或一列,该行或列只包含白色方块,将其移除并删除行或列之间的空格。
可以证明,无论选择每次操作中的哪一行或列,最终的网格状态都是唯一确定的。请找出网格的最终状态。
思路
先对行做筛选,把符合条件的复制的新的字符串里,
再对新的字符串做筛选,把符合条件的复制的新的字符串里
10.鹿每秒走一个单位格
题目
AtCoDeer是一只鹿,他计划在二维平面上进行一次旅行。
根据他的计划,他将在时间0从点(0,0)出发,然后对于每个i在1到N之间(包括1和N),他将在时间ti到达点(xi, yi)。
如果AtCoDeer在时间t处于点(x, y),那么在时间t+1,他可以处于以下四个点之一:(x+1, y),(x-1, y),(x, y+1)和
(x, y-1)。
请确定他能否执行他的计划。
int main()
{
int i, n;
scanf("%d", &n);
int x[n + 1], y[n + 1], t[n + 1];
x[0] = y[0] = t[0] = 0;
for (i = 1; i <= n; i++)
{
scanf("%d %d %d", &t[i], &x[i], &y[i]);
}
for (i = 1; i <= n; i++)
{
if (abs(x[i] - x[i - 1]) + abs(y[i] - y[i - 1]) > t[i] - t[i - 1] || (x[i] - x[i - 1] + y[i] - y[i - 1] + t[i] - t[i - 1]) % 2 != 0)
// // ||后面的部分: 检查当前点与上一个点的横坐标和纵坐标的差值之和与时间差值的奇偶性是否相同
{
printf("No\n");
return 0;
}
}
printf("Yes\n");
}
11.keyence字符串子串比较
题目
给定一个由小写英文字母组成的字符串 S,判断 S 是否是一个 KEYENCE 字符串。
KEYENCE 字符串是指可以通过仅删除其中一个连续子串(可能为空)将其变为 “keyence” 的字符串。
思路
计算2个不一样地方的下标和,注意审题~
int main()
{
int i, j;
char s[101];
char key[] = "keyence";
scanf("%s", s);
int n = strlen(s);
for (i = 0; i < 7; i++)
{
if (s[i] != key[i])
break;
}
for (j = 0; j < 7; j++)
{
if (s[n - 1 - j] != key[6 - j])
break;
}
if (i + j >= 7)
printf("YES\n");
else
printf("NO\n");
}
12.组合n个数字相加等于某个数字
12.1九宫格问题,找到6个数字加起来等于表格里的数字
题目
我们有一个3×3的网格。
数字c[i][j]写在方格(i,j)中,其中(i,j)表示从上方数第i行、从左边数第j列的方格。
根据Takahashi的说法,有六个固定的整数a1,a2,a3,b1,b2,b3,
而写在方格(i,j)中的数字等于ai+bj。
确定他是否正确。
思路
需要的6个数字并不是固定的,差值相同
任意自定义一个a[0],剩下的5个就都有了 然后看相加是否等于表格里的
int main()
{
int c[3][3], i, j, a[3], b[3];
int sum = 0;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
scanf("%d", &c[i][j]);
a[0] = 100;
for (i = 0; i < 3; i++)
b[i] = c[0][i] - a[0];
for (i = 1; i < 3; i++)
a[i] = c[i][0] - b[0];
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
if (a[i] + b[j] != c[i][j])
{
printf("No\n");
return 0;
}
}
}
printf("Yes\n");
}
12.2 3种不同的箱子组合起来装n个球
题目
Snuke在一家售卖盒子里装有球的商店里。该商店出售以下三种类型的盒子:
红盒,每个盒子里装有R个红球
绿盒,每个盒子里装有G个绿球
蓝盒,每个盒子里装有B个蓝球
Snuke想要通过购买r个红盒,g个绿盒和b个蓝盒总共获得恰好N个球。
有多少个非负整数三元组(r, g, b)可以实现这一点呢?
思路
笨方法 3冲循环套用,但是时间复杂度太大
在最内层的循环,选择用if将b变量作为被取模的数做比较
int main()
{
int r, g, b, n, i, j, k;
long long int sum = 0;
scanf("%d %d %d %d", &r, &g, &b, &n);
for (i = 0; i <= n / r; i++)
for (j = 0; j <= (n - i * r) / g; j++)
if ((n - i * r - j * g) % b == 0)//用剩余的数与需要循环的变量相对比减少了一重循环
{
sum++;
}
printf("%lld\n", sum);
}
13.用输入的数组当做下标的用来统计有多少不同数字的替代方法
题目
Joisino宣布一个数字。
如果该数字在纸上写过,就从纸上擦掉;
如果没有写过,就将数字写在纸上。
这个过程重复进行N次。
然后,你会被问一个问题:现在纸上写了多少个数字?
Joisino宣布的数字按照A1,…,AN的顺序给出。
在游戏结束时,纸上会写下多少个数字?
int cmp(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
int main()
{
int n, i;
scanf("%d", &n);
long long int a[n], count, ans = 0;
for (i = 0; i < n; i++)
scanf("%lld", &a[i]);
qsort(a, n, sizeof(long long int), cmp);
for (i = 0; i < n; i++)
{
count = 1;
while (a[i] == a[i + 1])
{
count++;
i++;
}
ans += count % 2;
}
printf("\n%lld\n", ans);
}
14.已知最小值和最大值,求这些数可能的和的数量–公式法
题目
nuke拥有N个整数。
在这些整数中,最小的是A,最大的是B。
我们对这N个整数的总和感兴趣。
有多少种不同可能的总和呢?
int main()
{
long long int n, a, b;
scanf("%lld %lld %lld", &n, &a, &b);
if ((n - 2) * (b - a) + 1 > 0)
{
printf("%lld\n", (n - 2) * (b - a) + 1);
return 0;
}
else
printf("0\n");
return 0;
}
15.最易混乱的一集–用i做变量的同时做下标且两者都变化
题目
你被给定一个长度为 N 的数组 A。
你的任务是将它分成若干连续的子数组。
在这里,所有得到的子数组必须按照非递减或非递增的顺序排序。
你至少需要将 A 分成多少个子数组?
思路
记住代码吧, 容易弄混i当前的值
int main()
{
int n, i, count = 0;
scanf("%d", &n);
long long int a[n];
for (i = 0; i < n; i++)
scanf("%lld", &a[i]);
for (i = 0; i < n; i++)
{
count++;
while (a[i] == a[i + 1])
{
i++;
}
if (a[i] < a[i+1])
{
while (a[i] <= a[i + 1])
{
i++;
}
}
else
while (a[i] >=a[i + 1])
i++;
}
printf("%d\n", count);
}
16.字符串比较
题目
Snuke 对满足以下条件的字符串感兴趣:
字符串的长度至少为 N。
前 N 个字符与字符串 s 相等。
后 N 个字符与字符串 t 相等。
找出满足上述条件的最短字符串的长度。
int main()
{
int n, i, j, SameNum = 0, count;
scanf("%d", &n);
char s[n + 1], t[n + 1];
scanf("%s %s", s, t);
if (strcmp(s, t) == 0)
{
printf("%d\n", n);
return 0;
}
for (i = 0; i < n; i++)
{
count = n - i;
SameNum = 0;
for (j = i; j < n; j++)
{
if (t[j - i] == s[j])
SameNum++;
}
if (SameNum == count)
{
printf("%d\n", 2 * n - count);
return 0;
}
}
printf("%d\n", 2 * n);
}
17.骰子期望值–时间复杂度
题目
我们有 N 个骰子从左到右排成一行。第 i 个骰子从左边开始投掷时,
以等概率显示从 1 到 pᵢ 的数字。
我们将选择 K 个相邻的骰子,分别投掷它们,然后计算所显示数字的和。
找出这个和的期望值的最大可能值。
思路
按照题面的意思,正常的思路会被卡时间
优化后的思路比较新,先计算每几个数的和,然后用fmax替换来更新max值
int main()
{
int i, j, n, k, max = 0;
scanf("%d %d", &n, &k);
int a[n], sum[n + 1];
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
for (i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i - 1] + 1;//存储了一个序列的前缀和
for (i = k; i <= n; i++)
max = fmax(max, sum[i] - sum[i - k]);//找到长度为 k 的连续子数组,使得它的部分和最大
printf("%.10lf\n", max / 2.0);
}
18.对字符串的qsort
> 参考:各种数据的qsort
题目
给予由小写英文字母组成的字符串 s 和 t。
你将通过自由重新排列 s 中的字符来创建一个新的字符串 s’。
你也将通过自由重新排列 t 中的字符来创建一个新的字符串 t’。
确定是否有可能满足条件 s’ < t’,即 s’ 在字典顺序上小于 t’。
int cmp1(const void *a, const void *b)
{
return *(char *)a - *(char *)b;
}
int cmp2(const void *a, const void *b)
{
return *(char *)b - *(char *)a;
}
int main()
{
char s[101], t[101];
scanf("%s", s);
getchar();
scanf("%s", t);
int len1 = strlen(s);
int len2 = strlen(t);
qsort(s, len1, sizeof(s[0]), cmp1);
qsort(t, len2, sizeof(t[0]), cmp2);
if (strcmp(s, t) < 0)
printf("Yes\n");
else
printf("No\n");
}
19.卡牌正面反面朝上–题目看不懂
题目
在一个有无限多行和列的网格中,有一个由连续的 N 行和 M 列组成的矩形区域,
在这个区域的每个方格里放着一张卡片。这些卡片有正反两面,最初每张卡片都是正面朝上的。
我们将按照以下方式为每个包含卡片的方格执行一次操作:
对于以下九个方格中的每一个,如果存在卡片,则将其中的卡片翻转:
目标方格本身以及与目标方格共享角落或边的八个方格。
可以证明,无论操作的顺序如何,经过所有操作后每张卡片是正面朝上还是背面朝上都不会改变。
找出经过所有操作后朝下的卡片数
思路
疑似公式?–也没看懂
计算在一个二维网格中,去掉了边界一圈后,剩余格子的数量的代码
n == 1 ? 1 : n - 2 去掉边界一圈后,剩余的行数。
如果 n 等于 1,那么只有一行,所以剩余行数为 1。
否则,从总行数 n 中减去 2,即去掉上下两行边界,得到剩余的行数。
(m == 1 ? 1 : m - 2) 这部分逻辑计算去掉边界一圈后,剩余的列数。
同样的逻辑,如果 m 等于 1,那么只有一列,所以剩余列数为 1。
否则,从总列数 m 中减去 2,即去掉左右两列边界,得到剩余的列数。
(剩余行数) * (剩余列数): 将剩余的行数和列数相乘,得到去掉边界一圈后,剩余格子的数量。
int main()
{
int n, m;
scanf("%d%d", &n, &m);
printf("%lld\n", (long long) (n == 1 ? 1 : n - 2) * (m == 1 ? 1 : m - 2));
}
20.猜数–给出每一位的数字
题目
如果存在一个大于等于0的整数满足以下条件,则输出最小的满足条件的整数;
否则,输出-1。
该整数在十进制下恰好有N位数
(我们认为0是一个1位数,对于其他整数,不允许有前导零)。
从左边开始数,第i位数字为cᵢ(i=1,2,⋯,M)。
思路
先对数据qsort,在看前后位数重复的数字是否相同,相同就存贮到num里
最后用atoi
只能通过大部分的txt,还有一部分无法通过,故无法ac
typedef struct
{
int cnt;
int digit;
} NUMBER;
int cmp(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
int main()
{
int n, m, i, figure;
scanf("%d %d", &n, &m);
NUMBER num[m];
for (i = 0; i < m; i++)
{
scanf("%d %d", &num[i].cnt, &num[i].digit);
num[i].cnt--;
}
char c[n];
for (i = 0; i < n; i++)
c[i] = '0'; // 初始化c里都是0
qsort(num, m, sizeof(NUMBER), cmp); // 按照cnt的大小排列了
for (i = 0; i < m; i++)
{
if (num[i].cnt == num[i + 1].cnt && num[i].digit != num[i + 1].digit)
{
printf("-1\n");
return 0;
}
c[num[i].cnt] = num[i].digit + '0';
}
figure = atoi(c);
printf("%d\n", figure);
return 0;
}
更新后
int main()
{
int n, m, i, s, c, result;
scanf("%d %d", &n, &m);
static int num[3];// 数组开得太大,不会提示内存超限; 而是会Runtime error
if (n == 1 && m == 0)
{
printf("0\n");
return 0;
}
memset(num, -1, n * sizeof(*num));
while (m--)
{
scanf("%d %d", &s, &c);
s--;
if ((n > 1 && s == 0 && c == 0) || (num[s] != -1 && num[s] != c))
{
printf("-1\n");
return 0;
}
//成功填充数组的一个元素
num[s] = c;
}
for (i = 0; i < n; i++)
{
if (num[i] != -1)
{
// 如果 num[i] 不等于 -1,表达式的值是 num[i]
result = num[i];
}
else
{
// 如果 num[i] 等于 -1,进一步判断 i 是否等于 0
if (i == 0)
// 如果 i 等于 0,表达式的值是 1
result = 1;
else
// 如果 i 不等于 0,表达式的值是 0
result = 0;
}
printf("%d", result);
}
putchar('\n');
}
2023年9月3日入队