PTA的题目是单点测试,所以写法上会有不同。
Exercise
Example
输入样例:
19 *
输出样例:
*****
***
*
***
*****
2
这题最坑的是每行输出字符结束后就直接换行,不输出空白符,眼神不好的就会犯错然后格式错误。
#include
int main()
{
int N, begin = 1, symbolUse = begin;
char c;
scanf("%d %c", &N, &c);
while (symbolUse <= N) {
begin += 2;
symbolUse += begin * 2;
}
symbolUse -= begin *2; // 总共要使用的符号数
begin -= 2; // 第一层要用的符号数
for (int k = begin; k >= 1; k-=2) {
int spaceNum = begin - k;
for (int w = 1; w <= spaceNum / 2; w++)
printf(" ");
for (int j = 1; j <= k; j++)
printf("%c", c);
printf("\n");
}
for (int k = 3; k <= begin; k+=2) {
int spaceNum = begin - k;
for (int w = 1; w <= spaceNum / 2; w++)
printf(" ");
for (int j = 1; j <= k; j++)
printf("%c", c);
printf("\n");
}
printf("%d\n", N - symbolUse);
return 0;
}
Exercise
让我们定义dn为:dn=pn+1−pn,其中pi是第i个素数
。显然有d1=1,且对于n>1有dn是偶数
。“素数对猜想”认为“存在无穷多对相邻且差为2
的素数”。现给定任意正整数N(<105),请计算不超过N的满足猜想的素数对的个数。
Example
输入样例:
20
输出样例:
4
My thoughts
我的想法是用筛法算出素数数组,然后一对对数。
#include
/* 素数对是指两个相邻素数间隔2,则2、3不是,3、5是 */
int main()
{
int N;
scanf("%d", &N);
const int room = N+10;
int isPrime[room];
for (int i = 2; i <= N; i++)
isPrime[i] = 1; // all are prime.
for (int i = 2; i <= N; i++) {
if (isPrime[i]) {
for (int j = i * 2; j <= N; j += i)
isPrime[j] = 0;
}
}
int count = 0;
for (int i = 2; i <= N-2; i++) {
if (isPrime[i] && isPrime[i+2])
count++;
}
printf("%d", count);
return 0;
}
Exercise
一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1⋯AN−1)变换为(AN−M⋯AN−1A0A1⋯AN−M−1)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
Example
输入格式:
每个输入包含一个测试用例,第1行输入N(1≤N≤100)和M(≥0);第2行输入N个整数,之间用空格分隔。
输出格式:
在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
输入样例:
6 2
1 2 3 4 5 6
输出样例:
5 6 1 2 3 4
My thoughts
这个题还有点奇怪,暴力的话可以直接用一个存储空间,将N个数不断向后移动一位,移动M次,时间复杂度为O(M * N)。缩小一下M的话可以求M = M Mod N。不过最好的还是以下的做法,这类的题目都可以这样做。
假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。变换的过程通过以下步骤完成:
- 逆序排列abcd:abcd1234 → dcba1234;
- 逆序排列1234:dcba1234 → dcba4321;
- 全部逆序:dcba4321 → 1234abcd。
#include
void reverse(int a[], int left, int right) {
// for (int i = left; i < (left + right + 1) / 2; i++) {
// int temp = a[i];
// a[i] = a[right-i];
// a[right-i] = temp;
// } 这样翻转写起来麻烦
for (int i = left, j = right; i < j; i++, j--) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
int main()
{
int N, M;
scanf("%d%d", &N, &M);
int a[N];
for (int i = 0; i < N; i++) {
scanf("%d", &a[i]);
}
// 有O(M*N)的方法, 也有以下的方法
M = M % N;
reverse(a, 0, N-M-1);
reverse(a, N-M, N-1);
reverse(a, 0, N-1);
for (int i = 0; i < N-1; i++)
printf("%d ", a[i]);
printf("%d", a[N-1]);
return 0;
}
这一道题目有一些值得我反思的地方,要详细分析一下。位数溢出,连long long
类型都超过了,只好使用字符数组模拟。
https://blog.csdn.net/royzdr/article/details/78902766.
1.
#include
#include
int main()
{
char s[30], db_s[30];
scanf("%s", s);
char *p = s;
int a[10], b[10];
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
while (*p) {
a[(*p - '0') % 10]++;
p++;
}
int le = strlen(s), carry = 0, j = 0; // 进位的数字
for (int i = le-1; i >= 0; i--, j++) {
int r = (s[i] - '0') * 2 + carry;
carry = r / 10;
db_s[j] = (char)('0' + r % 10);
b[r % 10]++;
}
if (carry) {
db_s[j] = (char)('0' + carry);
db_s[j+1] = '\0';
} else db_s[j] = '\0';
int YesNo = 1;
for (int k = 0; k < 10; k++) {
if (a[k] != b[k]) {
YesNo = 0;
break;
}
}
int len = strlen(db_s);
if (YesNo) {
printf("Yes\n");
for (int i = len-1; i >= 0; i--, j++)
printf("%c", db_s[i]);
}
else {
printf("No\n");
for (int i = len-1; i >= 0; i--, j++)
printf("%c", db_s[i]);
}
return 0;
}
这是一个自动洗牌机,牌初始的顺序我直接写在了代码里面,然后按照给定的顺序将牌的位置变为指定位置,并且对所有牌执行K次。If the number at the i-th position is j, it means to move the card from position i to position j
,将位于第i项的牌移动到给定位置去,特别注意:在操作牌号数组时牌的编号要减1。
一开始我想了蛮久,(ˇˍˇ) 想直接在字符串数组内部移动,后来发现可能有环路,很麻烦,然后我发现,这种操作是将一张牌的位置改变为指定位置,干脆直接开一个临时字符串数组,每次将cards[i]移动到temp[rotate[i]]中,完成后再复制回来。就这么简单。
#include
using namespace std;
int main() {
int K;
scanf("%d", &K);
string cards[54] = {
"S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "S12", "S13",
"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "H10", "H11", "H12", "H13",
"C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12", "C13",
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "D10", "D11", "D12", "D13",
"J1", "J2"};
int rotate[54];
string temp[54];
for (int i = 0; i < 54; i++) {
scanf("%d", &rotate[i]);
rotate[i] = rotate[i] - 1;
}
while (K--) {
for (int i = 0; i < 54; i++) {
temp[rotate[i]] = cards[i];
}
for (int i = 0; i < 54; i++) {
cards[i] = temp[i];
}
}
for (int i = 0; i < 54; i++) {
if (i > 0) cout << " ";
cout << cards[i];
}
return 0;
}
#include
int main()
{
int n, step = 0;
scanf("%d", &n); //输入题目给的n
while (n != 1) { //n不等于时循环
if (n % 2) n = (3 * n + 1) / 2; //是奇数
else n /= 2; //是偶数
step++; // 计数器加1
}
printf("%d\n", step);
return 0;
}
读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字。
输入格式:每个测试输入包含 1 个测试用例,即给出自然数n的值。这里保证n小于10100。
输出格式:在一行内输出 n 的各位数字之和的每一位,拼音数字间有 1 空格,但一行中最后一个拼音数字后没有空格。
这道题输入的数字太大,必须用字符串。且输出格式有点要求,可以像我下面这么做。
#include
#include
int main()
{
char s[120];
scanf("%s", s);
int len = strlen(s);
char r[][6] = {
"ling", "yi", "er", "san", "si",
"wu", "liu", "qi", "ba", "jiu"
};
int carry_sum = 0; // 每位数的和不大
for (int i = 0; i < len; i++)
carry_sum += (s[i] - '0');
int a[5], t = 0;
while (carry_sum) { //将sum中的每一位存到数组中, 顺位存储
a[t] = carry_sum % 10;
carry_sum /= 10;
t++;
}
printf("%s", r[a[t - 1]]); // 第一个拼音
for (int i = t - 2; i >= 0; i--)
printf(" %s", r[a[i]]);
printf("\n");
return 0;
}
有难度。
#include
#include
int main()
{
int T;
scanf("%d", &T);
while (T--) {
char s[120];
scanf("%s", s);
int len = strlen(s);
int num_p = 0, num_t = 0, other = 0; //代表P的个数、T的个数、除PAT外字符的个数
int loc_p = -1, loc_t = -1; //分别代表P的位置、T的位置
for (int i = 0; i < len; i++) {
if (s[i] == 'P') { //当前字符为P
num_p++; //个数+1
loc_p = i; //位置变为i
} else if (s[i] == 'T') { //当前字符为T
num_t++; //个数+1
loc_t = i; //位置变为i
} else if (s[i] != 'A') other++; //除PAT外字符的个数+1
}
//如果P的个数不为1; 或者T的个数不为1; 或者存在除PAT之外的字符; 或者P和T之间没有字符
if (num_p != 1 || num_t != 1 || other != 0 || loc_t - loc_p <= 1) {
printf("NO\n"); continue;
}
int x = loc_p, y = loc_t - loc_p - 1, z = len - loc_t - 1;
if (z - x * (y - 1) == x) { //条件2成立的思路
printf("YES\n");
} else printf("NO\n");
}
return 0;
}
这个题目说简单也简单,只要利用结构体变量间可以赋值的事实
就可以很快做出来。
这里我令结构体变量stu记录临时存放的数据,max、min则存放成绩最高和最低的学生信息。首先将max、min初始化,可以是-1(max)和101(min)(毕竟这里成绩为0-100
);或者像我一样干脆初始化为第一个学生的信息。然后一边录入一边更新,Online算法。这里注意的是比较新学生和max、min的成绩时不会同时更新两个,因此可以用if-else(if),也可以都用if
。
如果采用结构体数组,至少要101的大小,因为题目中说不会出现相同的成绩。这样浪费了空间。如果还进行排序的话,就连时间也浪费了。因此不推荐这两种方法。
B1028和这道题很像,但是更难一点。
#include
struct student {
char name[15];
char id[15];
int score;
}stu, max, min;
int main()
{
int n;
scanf("%d", &n);
scanf("%s %s %d", stu.name, stu.id, &stu.score);
max = min = stu;
for (int i = 1; i < n; i++) {
scanf("%s %s %d", stu.name, stu.id, &stu.score);
if (stu.score > max.score) max = stu;
if (stu.score < min.score) min = stu;
}
printf("%s %s\n%s %s\n", max.name, max.id, min.name, min.id);
return 0;
}
卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对n=3进行验证的时候,我们需要计算3、5、8、4、2、1,则当我们对n=5、8、4、2进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这4个数已经在验证3的时候遇到过了,我们称5、8、4、2是被3“覆盖”的数。我们称一个数列中的某个数n为“关键数”,如果n不能被数列中的其他数字所覆盖。
现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
6
3 5 6 7 8 11
7 6
这里有几个地方要注意:
#include
#include
using namespace std;
bool cmp(int a, int b) { return a > b; }
int hash[10500] = {0};
/* 如果n不能被数列中的其他数字所覆盖, 则n为“关键数”, 即数列中其他数计算时不会计算到它 */
void Callatz(int n) {
while (n != 1) { //n不等于时循环
if (n % 2) n = (3 * n + 1) / 2; //是奇数
else n /= 2; //是偶数
hash[n] = 0; //在计算某一个数的卡拉兹数列时被覆盖
}
}
int main() {
int K, cnt = 0;
scanf("%d", &K);
int a[K];
for (int i = 0; i < K; i++) { scanf("%d", &a[i]); hash[a[i]] = 1; }
sort(a, a + K, cmp); //从大到小排序
for (int i = 0; i < K; i++) Callatz(a[i]);
for (int i = 0; i < K; i++) {
if (hash[a[i]] != 0) {
if (cnt == 0) { printf("%d", a[i]); cnt++; }
else printf(" %d", a[i]);
}
}
return 0;
}
#include
int main() {
char str[50];
int n, size = 0, g, s, b;
scanf("%d", &n);
g = n % 10, s = n % 100 / 10, b = n / 100;
for (int i = 1; i <= b; i++) str[size++] = 'B';
for (int i = 1; i <= s; i++) str[size++] = 'S';
for (int i = 1; i <= g; i++) str[size++] = i + '0';
str[size] = '\0';
printf("%s\n", str);
return 0;
}
按筛法解题,在上面做过的现题目,感觉上面写的还简单一点,不是生搬硬套:
#include
const int maxn = 100010;
int prime[maxn], p[maxn] = {0}, pNum = 0;
void findPrime(int n) { //大于n时退出
for (int i = 2; i < maxn; i++) {
if (i > n) break;
if (p[i] == 0) {
prime[pNum++] = i;
for (int j = i + i; j < maxn; j += i) {
p[j] = 1;
}
}
}
}
int main() {
int n;
scanf("%d", &n);
findPrime(n);
int ans = 0; //素数对个数
for (int i = 0; i < pNum - 1; i++) {
if (prime[i + 1] - prime[i] == 2) ans++;
}
printf("%d\n", ans);
return 0;
}
在不允许使用另外数组的前提下。这道题也做过,就在上边,知道是用三步翻转法。可是竟然还会错,原因在于忘记翻转数组的时候下标必须合法, 对于M>=N的必须取余使之合法。
因为本题并未给出M的最大值,不能直接认定M
#include
/* 将数组a的i到j位翻转[i, j] */
void reverseArray(int a[], int i, int j)
{
for (int left = i, right = j; left < right; left++, right--) {
int temp = a[left];
a[left] = a[right];
a[right] = temp;
}
}
int main()
{
int N, M;
scanf("%d%d", &N, &M);
int a[N];
for (int i = 0; i < N; i++)
scanf("%d", &a[i]);
M = M % N; /* 翻转数组的时候下标必须合法, 对于M>N的必须取余使之合法 */
reverseArray(a, 0, N - M - 1);
reverseArray(a, N - M, N - 1);
reverseArray(a, 0, N - 1);
printf("%d", a[0]);
for (int i = 1; i < N; i++)
printf(" %d", a[i]);
return 0;
}
更加tricky的方法是,既然这种题目没有要求过程而只要结果,那我们就直接给它结果。
#include
int main()
{
int N, M;
scanf("%d%d", &N, &M);
int a[N];
for (int i = 0; i < N; i++)
scanf("%d", &a[i]);
M = M % N;
//第一个for循环输出N-M到N-1号, 不可能将数组输出完, 不用处理最后一个数的格式
for (int i = N - M; i < N; i++)
printf("%d ", a[i]);
for (int i = 0; i < N - M - 1; i++)
printf("%d ", a[i]);
printf("%d", a[N - M - 1]); // 输出最后一个数
return 0;
}
这题不知道为什么写成while (scanf("%s", s[len++]) != EOF);
就死活不通过。以后还是按照下面写吧。
而且,我发现写成for (; scanf("%s", s[len]) != EOF; len++);
这样也可以通过……
#include
int main()
{
char s[100][100];
int len = 0;
while (scanf("%s", s[len]) != EOF) { //一直输入到文件末尾
len++;
}
for (int i = len - 1; i >= 1; i--) //倒着输出单词
printf("%s ", s[i]);
printf("%s", s[0]);
return 0;
}
法2:将输入的字符串分割成单词。
#include
#include
int main()
{
char str[90], s[100][100]; //s存放单词
gets(str);
int len = strlen(str), r = 0, c = 0; //r行标 c列标
for (int i = 0; i < len; i++) {
if (str[i] != ' ') { //不是空格存放进s[r][c], c++
s[r][c++] = str[i];
} else { //是空格, 说明一个单词结束, 行r+1, 列c置0
s[r][c] = '\0'; //末尾是结束符'\0'
r++;
c = 0;
}
}
for (int i = r; i >= 0; i--) { //倒着输出单词
printf("%s", s[i]);
if (i > 0) printf(" ");
}
return 0;
}
从低次项至高次项进行枚举求导,通过求导公式修改数组的元素,同时计数系数不为0的导数项个数。
#include
int main()
{
int coef[1010] = {0}; //对应指数e的系数数组
int co, ef, cnt = 0; //系数 指数 不为0的导数项的个数
while (scanf("%d%d", &co, &ef) != EOF) coef[ef] = co;
coef[0] = 0; //0次项求导后系数直接为0
for (int i = 1; i <= 1000; i++) {
coef[i - 1] = coef[i] * i; //求导公式, 从低次项向高次项枚举
coef[i] = 0; //此句不可省略
if (coef[i - 1] != 0) cnt++; //计数系数不为0的导数项个数
}
if (cnt == 0) printf("0 0"); //特判, 全部导数项系数为0
else {
for (int i = 1000; i >= 0; i--) { //按指数从高到低输出系数和指数
if (coef[i] != 0) {
printf("%d %d", coef[i], i);
cnt--;
if (cnt > 0) printf(" ");
}
}
}
return 0;
}
CodeUp
做过的一道题。
题目给的范围是[-231, 231], 需知道int的数据范围是[-231,231-1]。可能会溢出。必须使用long long类型作为ABC的变量类型,输入输出格式必须是"%lld"。
#include
int main()
{
int T, tcase = 1;
scanf("%d", &T);
while (T--) {
long long a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
if (a + b > c) printf("Case #%d: true\n", tcase++);
else printf("Case #%d: true\n", tcase++);
}
return 0;
}
CodeUp
做过的一道题。
不过这里优化一下写法。在判断某类数字是否存在时使用数组count,count为0即不存在。对五类数字的结果使用ans数组。两者初值均为0。
#include
int main()
{
int N, a;
int ans[5] = {0}, count[5] = {0};
scanf("%d", &N);
int flag = 1;
for (int i = 0; i < N; i++) {
scanf("%d", &a);
switch (a % 5) {
case 0: //A1类
if (a % 2 == 0) {
ans[0] += a;
count[0]++;
}
break;
case 1: //A2类
if (flag) {
ans[1] += a;
count[1]++;
flag = 0;
} else {
ans[1] += -a;
count[1]++;
flag = 1;
}
break;
case 2: //A3类
ans[2]++;
count[2]++;
break;
case 3: //A4类
ans[3] += a;
count[3]++;
break;
case 4: //A5类
if (a > ans[4])
ans[4] = a;
count[4]++;
break;
}
}
if (count[0]) printf("%d ", ans[0]);
else printf("N ");
if (count[1]) printf("%d ", ans[1]);
else printf("N ");
if (count[2]) printf("%d ", ans[2]);
else printf("N ");
if (count[3]) printf("%.1f ", (double)ans[3] / count[3]);
else printf("N ");
if (count[4]) printf("%d", ans[4]); //最后一个输出不能有空格
else printf("N");
return 0;
}
这一题只要求输出第m到第n个素数,超过n个素数之后的就不用保存了,因此添加了控制语句。而考虑到不知道第104个素数有多大,因此设置测试上限设得大了一些。
#include
const int maxn = 1000001;
int prime[maxn] = {0}, pNum = 0;
bool p[maxn] = {0};
void find_prime(int n) { //找到第n个素数后退出
for (int i = 2; i < maxn; i++) {
if (p[i] == false) { //说明它是素数
prime[pNum++] = i;
if (pNum >= n) break; //只需要n个素数, 超过时就可以结束
for (int j = i + i; j < maxn; j += i) {
p[j] = true;
}
}
}
}
int main() {
int m, n, cnt = 0;
scanf("%d%d", &m, &n);
find_prime(n);
for (int i = m - 1; i <= n - 1; i++) {
printf("%d", prime[i]); //下标从0开始
cnt++;
if (cnt % 10 != 0 && i < n - 1) printf(" ");
else printf("\n");
}
return 0;
}
大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm
。大侦探很快就明白了,字条上奇怪的乱码实际上就是约会的时间星期四 14:04,因为前面两字符串中第1对相同的大写英文字母(大小写有区分
)是第4 个字母D,代表星期四;第2对相同的字符是E,那是第5个英文字母,代表一天里的第14个钟头(于是一天的0点到23点由数字0到9、以及大写字母A到N表示);后面两字符串第1对相同的英文字母s出现在第4个位置(从0开始计数)上,代表第4分钟。现给定两对字符串,请帮助福尔摩斯解码得到约会的时间。
3485djDkxh4hhGE
2984akDfkkkkggEdsb
s&hgsfdk
d&Hyscvnm
THU 14:04
这道题还是有一些要注意的地方的:
#include
char weekday[][6] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
int main() {
char s[4][70];
for (int i = 0; i < 4; i++) scanf("%s", s[i]);
int i, day, h, m, cnt = 1;
for (i = 0; s[0][i] && s[1][i]; i++) {
if (cnt == 1 && s[0][i] >= 'A' && s[0][i] <= 'G' && s[0][i] == s[1][i]) { day = s[0][i] - 'A'; cnt++; }
else if (cnt == 2 && (s[0][i] >= '0' && s[0][i] <= '9' || s[0][i] >= 'A' && s[0][i] <= 'N') && s[0][i] == s[1][i]) {
h = s[0][i] >= 'A' ? s[0][i] - 'A' + 10 : s[0][i] - '0'; break;
}
}
for (i = 0; s[2][i] && s[3][i]; i++) {
if ((s[2][i] >= 'A' && s[2][i] <= 'Z' || s[2][i] >= 'a' && s[2][i] <= 'z') && s[2][i] == s[3][i]) { m = i; break; }
}
printf("%s %02d:%02d\n", weekday[day], h, m);
return 0;
}
写排序函数的经典题目。这一题中,没有达到合格标准的考生,我都没有存到结构体数组中去,毕竟多写几句,排序的规模说不定小一些。
#include
#include
#include
using namespace std;
struct student {
char id[10];
int d, c, sum;
int type; //类别
} stu[100100];
bool cmp(struct student a, struct student b) {
if (a.type != b.type) return a.type < b.type; //类别小的在前
else if (a.sum != b.sum) return a.sum > b.sum; //类别相同时总分大的在前
else if (a.d != b.d) return a.d > b.d; //总分相同时德分大的在前
else return strcmp(a.id, b.id) < 0; //德分相同时按准考证号从小到大排序
}
int main() {
int N, L, H, legalNum = 0;
scanf("%d%d%d", &N, &L, &H);
struct student temp;
for (int i = 0; i < N; i++) {
scanf("%s%d%d", temp.id, &temp.d, &temp.c);
temp.sum = temp.d + temp.c;
if (temp.d < L || temp.c < L) continue;
else if (temp.d >= H && temp.c >= H) {
temp.type = 1; //德分和才分均不低于此线的为才德全尽
stu[legalNum++] = temp;
} else if (temp.d >= H && temp.c < H) {
temp.type = 2; //才分不到但德分到线的一类考生属于德胜才
stu[legalNum++] = temp;
} else if (temp.d < H && temp.c < H && temp.d >= temp.c) {
temp.type = 3; // 德才分均低于H,但是德分不低于才分的考生属于才德兼亡但尚有德胜才
stu[legalNum++] = temp;
} else {
temp.type = 4; //其他达到最低线L的考生也按总分排序
stu[legalNum++] = temp;
}
}
sort(stu, stu + legalNum, cmp);
printf("%d\n", legalNum);
for (int i = 0; i < legalNum; i++) {
printf("%s %d %d\n", stu[i].id, stu[i].d, stu[i].c);
}
return 0;
}
我在CodeUp
使用的是字符串存储,枚举字符串中出现该字符的数码,然后恢复成数字。这里我用的方法更简单,只要用12行就可以了。使用long long存放A和B,枚举A和B中的每一位,如果该位恰好等于DA,就让PA = PA * 10 + DA。
#include
int main()
{
long long a, b, da, db;
scanf("%lld%lld%lld%lld", &a, &da, &b, &db);
long long pa = 0, pb = 0;
while (a) { // 枚举a的每一位
if (a % 10 == da) pa = pa * 10 + da;
a /= 10;
}
while (b) { // 枚举b的每一位
if (b % 10 == db) pb = pb * 10 + db;
b /= 10;
}
printf("%lld\n", pa + pb);
return 0;
}
这道题为高精度整数与低精度整数的除法操作,可以直接使用书中的模版。
#include
#include
const int maxn = 1010;
struct bign {
int d[maxn], len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char s[]) {
bign a;
a.len = strlen(s);
for (int i = 0; s[i]; i++) {
a.d[i] = s[a.len - i - 1] - '0';
}
return a;
}
bign divide(bign a, int b, int &r) {
bign c; c.len = a.len; //被除数的每一位和商的每一位一一对应, 因此先令长度相等
for (int i = a.len - 1; i >= 0; i--) { //从高位开始
r = r * 10 + a.d[i]; //和上一代的余数结合
c.d[i] = r / b; //商
r = r % b; //不改变a, 而是改变r
}
while (c.d[c.len - 1] == 0 && c.len > 1) c.len--;
return c;
}
void print(bign a) {
for (int i = a.len - 1; i >= 0; i--) {
printf("%d", a.d[i]);
}
}
int main() {
char A[1010];
int B, r = 0;
scanf("%s%d", A, &B);
bign a = change(A);
print(divide(a, B, r)); //r以初值0传入
printf(" %d", r);
return 0;
}
CodeUp
做过这个题目。我的这个思路已经很好了,很直接,是比较容易理解的方法,通过switch-case在九种情况间跳转,记录甲乙赢的次数和平局的次数,就可以推断出甲乙输的次数。考虑到最后要输出字典序最小的解,干脆一开始就将三种手势按字典序排好,即BCJ,然后分别记录两人用这三种手势赢的次数,比较得到胜利次数最多的手势时,先设其为B,只有当C的次数真的大于B的时候,才更新B为C,同理将C更新为J。
#include
char c[] = {'B', 'C', 'J'}; //c[0] = 'B'...
/* 返回获胜次数最多的手势,如果解不唯一,则返回按字母序最小的手势字符 */
char checkWinWay(int m[])
{ // B C J
int k = 0;
for (int i = 1; i < 3; i++)
if (m[i] > m[k]) k = i;
return c[k];
}
int main()
{
int N, winJ, winY, par, J[3] = {0}, Y[3] = {0}; // B C J
winJ = winY = par = 0;
char a, b;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
getchar(); // 吸收换行
scanf("%c %c", &a, &b);
switch(a) {
case 'C':
switch(b) {
case 'C': par++; break;
case 'J': winJ++; J[1]++; break;
case 'B': winY++; Y[0]++; break;
}
break;
case 'J':
switch(b) {
case 'C': winY++; Y[1]++; break;
case 'J': par++; break;
case 'B': winJ++; J[2]++; break;
}
break;
case 'B':
switch(b) {
case 'C': winJ++; J[0]++; break;
case 'J': winY++; Y[2]++; break;
case 'B': par++; break;
}
break;
}
}
printf("%d %d %d\n%d %d %d\n", winJ, par, N-winJ-par, winY, par, N-winY-par);
printf("%c %c\n", checkWinWay(J), checkWinWay(Y));
return 0;
}
还可以更加简化一下switch-case这一段,太长了。我们预先将三种手势按字典序排好后,发现这个顺序正好是循环相克顺序,即B胜C、C胜J、J胜B。因此可以提供一个映射函数将BCJ分别对应到0、1、2,对每组手势的比较先将其转换为数字再判断,由于设置的顺序恰好是循环相克顺序,因此c1胜c2的条件是(c1+1) % 3 == c2
,平的条件是c1 == c2
,输的条件(也是c2胜c1的条件)是(c2+1) % 3 == c1
。这样就可以减少代码长度了。不过,switch-case的代码在时间复杂度上面可能会更优秀。
#include
char c[] = {'B', 'C', 'J'}; //c[0] = 'B'...
char checkWinWay(int m[]); //...略去, 代码见上
int change(char c)
{ /* 提供手势字符到数字的转换 */
return c == 'B' ? 0 : c == 'C' ? 1 : 2;
}
int main()
{ //winNote按顺序记录甲赢、乙赢、平局的次数; J和Y分别记录甲乙按B、C、J赢的次数
int N, winNote[3] = {0}, J[3] = {0}, Y[3] = {0};
char a, b;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
getchar(); //吸收上一句的换行
scanf("%c %c", &a, &b);
int k1 = change(a), k2 = change(b);
if ((k1 + 1) % 3 == k2) { //甲赢
winNote[0]++; //甲赢次数+1
J[k1]++; //甲靠k1赢的次数+1
} else if ((k2 + 1) % 3 == k1) { //乙赢
winNote[1]++; //乙赢次数+1
Y[k2]++;
} else //平局
winNote[2]++;
}
printf("%d %d %d\n", winNote[0], winNote[2], N-winNote[0]-winNote[2]);
printf("%d %d %d\n", winNote[1], winNote[2], N-winNote[1]-winNote[2]);
printf("%c %c\n", checkWinWay(J), checkWinWay(Y));
return 0;
}
C++的sort函数真的是实用。本题的关键在于将数字转换成数组,排序得到递增和递减数组,并将之转化回数字,得到最小数和最大数。接下来就简单了。
#include
#include
using namespace std;
bool cmp(int a, int b) {
return a > b; //递减排序函数
}
void toArray(int n, int nums[]) {
for (int i = 0; i < 4; i++) {
nums[i] = n % 10; //如果某步为3位数, 则视作高位补0
n /= 10;
}
}
int toNumber(int nums[]) {
int sum = 0;
for (int i = 0; i < 4; i++) {
sum = sum * 10 + nums[i];
}
return sum;
}
int main() {
int N, min, max;
scanf("%d", &N);
int nums[4];
while (1) {
toArray(N, nums); //将N转换为数组
sort(nums, nums + 4); //递增排序
min = toNumber(nums); //得到最小值
sort(nums, nums + 4, cmp); //递减排序
max = toNumber(nums); //得到最大值
N = max - min; //得到下一个数
printf("%04d - %04d = %04d\n", max, min, N);
//如果下一步是0, 说明该数全部位相同, 按题意输出后退出; 如果是6174则结束
if (N == 0 || N == 6174) break;
}
return 0;
}
先卖出单价最高的月饼,然后单价次之的月饼。
#include
#include
using namespace std;
struct mooncake {
double price; //单价
double sell; //总售价
double store; //库存量
} cake[1010];
bool cmp(struct mooncake a, struct mooncake b) {
return a.price > b.price; //按单价从高到低排序
}
int main() {
int N;
double D;
scanf("%d%lf", &N, &D);
for (int i = 0; i < N; i++) {
scanf("%lf", &cake[i].store);
}
for (int i = 0; i < N; i++) {
scanf("%lf", &cake[i].sell);
cake[i].price = cake[i].sell / cake[i].store; //计算单价
}
sort(cake, cake + N, cmp);
double revenue = 0; //计算收益
for (int i = 0; i < N; i++) {
if (cake[i].store <= D) { //如果需求量高于月饼库存量
D -= cake[i].store;
revenue += cake[i].sell; //第一种月饼全部卖出
} else {
revenue += cake[i].price * D; //只卖出剩余需求量的月饼;
break;
}
}
printf("%.2f\n", revenue);
return 0;
}
用字符数组的形式存储题目给定的正整数N,用直接映射的哈希表记录0-9数字出现的次数,打印次数不为0的那些数。
#include
int main() {
char N[1020];
int hashTable[10] = {0}; //0 1 2 3 4 5 6 7 8 9
scanf("%s", N);
for (int i = 0; N[i]; i++) hashTable[N[i] - '0']++;
for (int i = 0; i < 10; i++) if (hashTable[i]) printf("%d:%d\n", i, hashTable[i]);
return 0;
}
#include
int main()
{
int a, b, sum, d, nums[100], i = 0;
scanf("%d%d%d", &a, &b, &d);
sum = a + b;
do {
nums[i++] = sum % d;
sum /= d;
} while (sum != 0);
for (int j = i - 1; j >= 0; j--)
printf("%d", nums[j]);
return 0;
}
#include
int main() {
int count[10]; //记录数字0-9个数
for (int i = 0; i < 10; i++)
scanf("%d", &count[i]);
for (int i = 1; i < 10; i++) { //从1-9中选择count不为0的最小的数字打印
if (count[i]) {
printf("%d", i);
count[i]--;
break;
}
}
for (int i = 0; i < 10; i++) { //从0-9中输出对应个数的数字
for (int j = 0; j < count[i]; j++) {
printf("%d", i);
}
}
return 0;
}
将一个符合科学记数法的字符串转换为浮点数的表示形式。
#include
#include
int main() {
char a[10100]; //实数A的存储长度不超过9999字节,且其指数的绝对值不超过9999
scanf("%s", a); //输入测试用例
/* 输出负号不输出正号 */
if (a[0] == '-') printf("-");
/* 第一步 找到E的位置*/
int pos = 0;
while (a[pos] != 'E') pos++;
/* 第二步 求出右边指数的绝对值大小, 先不管符号 */
int exp = 0;
for (int i = pos + 2; a[i]; i++) {
exp = exp * 10 + a[i] - '0';
} //特判指数为0的情况
if (exp == 0) for (int i = 1; i < pos; i++) printf("%c", a[i]);
/* 第三步 根据指数正负分类输出 */
else if (a[pos + 1] == '-') { //指数为负
printf("0.");
for (int i = 0; i < exp - 1; i++) printf("0"); //输出普通数字表示法的小数点后面exp-1个连续的0
/* 输出字母E之前的所有数字 */
printf("%c", a[1]); //输出科学计数法小数点前面的1个数字
for (int i = 3; i < pos; i++) printf("%c", a[i]); //输出除了小数点外的数字
} else { //指数为正
for (int i = 1; i < pos; i++) { //输出小数点移动后的数
if (a[i] == '.') continue; //掠过小数点
printf("%c", a[i]); //输出当前数位
if (i == exp + 2 && pos - 3 != exp) printf(".");//在输出(exp + 2)位置上的数后面添加小数点
//原小数点和E之间的数字个数(pos - 3)不能等于小数点右移位数expe, 不然就不用输出小数点了
}
for (int i = 0; i < exp - (pos - 3); i++) printf("0"); //如果指数够大, 输出多余的0
}
return 0;
}
给定一个常数K以及一个单链表L,请编写程序将L中每K个结点反转。例如:给定L为1→2→3→4→5→6,K为3,则输出应该为3→2→1→6→5→4;如果K为4,则输出应该为4→3→2→1→5→6,即最后不到K个元素不反转。
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1
分析:输入样例正确连接顺序应该是:
00100 1 12309
12309 2 33218
33218 3 00000
00000 4 99999
99999 5 68237
68237 6 -1
还应该考虑输入样例中有不在链表中的结点的情况。所以用个sum计数。而且,algorithm头文件里面有reverse函数可以直接调用。
#include
using namespace std;
const int maxn = 100005;
//静态链表
struct node {
int data, next;
} List[maxn];
int main() {
int first, n, k, temp;
scanf("%d%d%d", &first, &n, &k);
while (n--) {
cin >> temp;
cin >> List[temp].data >> List[temp].next;
}
int sum = 0, link[maxn]; //记录存在链表中的元素个数
while (first != -1) {
link[sum++] = first;
first = List[first].next; //记录头指针和链接
}
for (int i = 0; i < sum - sum % k; i += k) //剩下的小于k的不反转
reverse(link + i, link + i + k);
for (int i = 0; i < sum - 1; i++)
printf("%05d %d %05d\n", link[i], List[link[i]].data, link[i + 1]);
printf("%05d %d -1", link[sum - 1], List[link[sum - 1]]); //打印最后一个结点
return 0;
}
给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而[0, K]区间内的元素都排在大于K的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为 18→7→-4→0→5→-6→10→11→-2,K为10,则输出应该为 -4→-6→-2→7→0→5→10→18→11。
输入格式:
每个输入包含1个测试用例。每个测试用例第1行给出:第1个结点的地址;结点总个数,即正整数N (<= 105);以及正整数K (<=1000)。结点的地址是5位非负整数,NULL地址用-1表示。
接下来有N行,每行格式为:
Address Data Next
其中Address是结点地址;Data是该结点保存的数据,为[-105, 105]区间内的整数;Next是下一结点的地址。题目保证给出的链表不为空。
输出格式:
对每个测试用例,按链表从头到尾的顺序输出重排后的结果链表,其上每个结点占一行,格式与输入相同。
输入样例:
00100 9 10
23333 10 27777
00000 0 99999
00100 18 12309
68237 -6 23333
33218 -4 00000
48652 -2 -1
99999 5 68237
27777 11 48652
12309 7 33218
输出样例:
33218 -4 68237
68237 -6 48652
48652 -2 12309
12309 7 00000
00000 0 99999
99999 5 23333
23333 10 00100
00100 18 27777
27777 11 -1
分析:将结点用list[10000]保存,list为node类型,node中保存结点的值value和它的next地址。list的下标就是结点的地址。将<0、0~k、>k三部分的结点地址分别保存在v[0]、v[1]、v[2]中,最后将vector中的值依次输出即可~
#include
#include
using namespace std;
struct node {
int data, next;
}list[100000];
vector<int> v[3];
int main() {
int start, n, k, a;
scanf("%d%d%d", &start, &n, &k);
for (int i = 0; i < n; i++) {
scanf("%d", &a);
scanf("%d%d", &list[a].data, &list[a].next);
}
int p = start;
while(p != -1) {
int data = list[p].data;
if (data < 0)
v[0].push_back(p);
else if (data >= 0 && data <= k)
v[1].push_back(p);
else
v[2].push_back(p);
p = list[p].next;
}
int flag = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < v[i].size(); j++) {
if (flag == 0) {
printf("%05d %d ", v[i][j], list[v[i][j]].data);
flag = 1;
} else {
printf("%05d\n%05d %d ", v[i][j], v[i][j], list[v[i][j]].data);
}
}
}
printf("-1");
return 0;
}
本题和C语言中测试函数时间的方式很接近,可以触类旁通。同时本题对四舍五入的方法、时间(秒、分、时)的换算、输出格式都有一定的要求。
#include
int main()
{
int c1, c2;
scanf("%d%d", &c1, &c2);
int ticks = (c2 - c1); //按题目要求做差
if (ticks % 100 >= 50) ticks = ticks / 100 + 1; // 末两位如果>=50, 则除以100后必须+1
else ticks /= 100; //四舍五入
printf("%02d:%02d:%02d", ticks / 3600, ticks % 3600 / 60, ticks % 3600 % 60);
return 0;
}
发现是道做过的题目。
#include
int main()
{
int n, useSymbols = 1, beginSym = 1;
char c;
scanf("%d %c", &n, &c);
/* beginSym用1个符号即沙漏中心, 要使用的符号数开始也是1, 然后逐层累计, 试出可以使用的最大符号数*/
while (useSymbols <= n) {
beginSym += 2;
useSymbols += (beginSym * 2);
}
useSymbols -= (beginSym * 2); // 可使用的最大符号数
beginSym -= 2; // 第一层和最后一层的符号数
/* 开始打印沙漏 */
for (int i = beginSym; i >= 1; i -= 2) {
int spaceNum = beginSym - i;
for (int k = 1; k <= spaceNum / 2; k++)
printf(" ");
for (int k = 1; k <= i; k++)
printf("%c", c);
printf("\n");
} // 上半部分结束, 打印下半部分
for (int i = 3; i <= beginSym; i += 2) {
int spaceNum = beginSym - i;
for (int k = 1; k <= spaceNum / 2; k++)
printf(" ");
for (int k = 1; k <= i; k++)
printf("%c", c);
printf("\n");
}
printf("%d\n", n - useSymbols);
return 0;
}
另一种思路是我这次做的,即将沙漏的变化视作两个相同等差数列an,可使用的最大符号数为2 * Sn -1
<= n。等差数列求和公式如下。计算出一部分的层数,即可求出第一层的符号数和可使用的最大符号数。
#include
#include
int main()
{
int n, useSymbols = 1, beginSym = 1;
char c;
scanf("%d %c", &n, &c);
// 视作一个等差数列和*2-1 <= n, 求出上半部分的层数
int level = (int)sqrt((n + 1) / 2);
beginSym = 1 + (level - 1) * 2;
useSymbols = level * level * 2 - 1;
...
这个题目在逻辑上面和B1004一致,大体过程也一样。但是要注意几点:
%d/%d/%d
的格式,这是我以前不太熟悉的;if-if
结构,不然会出错。#include
struct person {
char name[10];
int yy, mm, dd; //日期
} oldest, youngest, left, right, temp; //前两者存放最年长和年轻的人, 后两者存放合法日期的左右界限
//1814年9月6日: left和youngest; 2014年9月6日: right和oldest
void init() {
youngest.yy = left.yy = 1814;
oldest.yy = right.yy = 2014;
youngest.mm = oldest.mm = left.mm = right.mm = 9;
youngest.dd = oldest.dd = left.dd = right.dd = 6;
}
bool LessEqu(person a, person b) { //a的日期小于等于b, 返回true(a早于b)
if (a.yy != b.yy) return a.yy <= b.yy;
else if (a.mm != b.mm) return a.mm <= b.mm;
else return a.dd <= b.dd;
}
bool MoreEqu(person a, person b) { //a的日期大于等于b, 返回true(a晚于b)
if (a.yy != b.yy) return a.yy >= b.yy;
else if (a.mm != b.mm) return a.mm >= b.mm;
else return a.dd >= b.dd;
}
int main() {
init();
int n, legal_num = 0; //legal_num存放合法日期的人数
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s %d/%d/%d", temp.name, &temp.yy, &temp.mm, &temp.dd);
if (MoreEqu(temp, left) && LessEqu(temp, right)) {
legal_num++;
if (LessEqu(temp, oldest)) oldest = temp; //更新oldest
if (MoreEqu(temp, youngest)) youngest = temp; //更新youngest
}
}
if (legal_num == 0) printf("0\n"); //所有人的日期都不合法, 只输出0
else printf("%d %s %s\n", legal_num, oldest.name, youngest.name);
return 0;
}
坏掉的键等价于原字符串中出现而实际输入了的字符串中没有出现的字符。旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现
。双指针分别指向原字符串和现字符串,可以用O(N)的时间解决问题。
#include
/* 整数/字符哈希题目 */
int main() {
char old[100], now[100];
scanf("%s%s", old, now);
int Hash[150] = {0};
for (int i = 0, j = 0; old[i] || now[j]; ) {
if (old[i] == now[j]) { i++; j++; }
else if (old[i] != now[j]) {
int t = (old[i] >= 'a' && old[i] <= 'z') ? old[i] - 'a' + 'A' : old[i];
if (Hash[t] == 0) { printf("%c", t); Hash[t] = 1; }
i++;
}
}
printf("\n");
return 0;
}
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
10 8
2 3 20 4 5 1 6 7 8 9
8
我自己提供一点测试样例,
3 3
1 3 3
3
注意点:
#include
#include
using namespace std;
/* 二分上下界为[left, right], left和right初始值覆盖解空间,
传入的初值为[0, n]; 获得数列中第一个大于x的位置 */
int binarySearch(long long a[], int left, int right, long long x) {
while (left < right) {
int mid = (left + right) / 2;
if (a[mid] > x) right = mid;
else if (a[mid] <= x) left = mid + 1;
}
return left;
}
int main() {
int n, p;
scanf("%d%d", &n, &p);
long long a[n];
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
sort(a, a + n);
int ans = 0;
for (int i = 0; i < n; i++) {
long long mp = a[i] * p; //p倍子数列最小值
int j = binarySearch(a, i + 1, n, mp);
ans = j - i > ans ? j - i : ans;
}
printf("%d\n", ans);
return 0;
}
用upper_bound替代的版本:
#include
#include
using namespace std;
int main() {
int n, p;
scanf("%d%d", &n, &p);
long long a[n];
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
sort(a, a + n);
long long ans = 0, mp; //p倍子数列最小值
for (int i = 0; i < n; i++) {
mp = a[i] * p;
long long *tail = upper_bound(a, a + n, mp); //找到连续递增子数列中mp的位置
ans = tail - (a + i) > ans ? tail - (a + i) : ans;
}
printf("%lld\n", ans);
return 0;
}
双指针的版本(O(N)
):
#include
#include
using namespace std;
int main() {
int n, p;
scanf("%d%d", &n, &p);
long long a[n];
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
sort(a, a + n);
int count = 0, i = 0, j = 0;
while (i < n && j < n) { //j先不断右移, 直到恰好不满足条件, 过程中更新count
while (j < n && a[j] <= (long long)a[i] * p) {
count = max(count, j - i + 1); //更新计数器
j++;
}
i++; //右移一位
}
printf("%d\n", count);
return 0;
}
注意点:
#include
char ZM[] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; //从0开始
int weight[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; //权重分配
int main() {
int flag = 1, N;
scanf("%d", &N);
char id[20];
while (N--) {
scanf("%s", id);
int sum = 0, f = 1; //对前17位数字加权求和
for (int i = 0; i <= 16; i++) { //检查前17位是否全为数字
if (id[i] > '9') { printf("%s\n", id); flag = f = 0; break; }
else sum += (id[i] - '0') * weight[i];
}
if (f) { //计算的和对11取模得到值Z
if (ZM[sum % 11] != id[17]) { //最后1位校验码计算不准确
printf("%s\n", id); flag = 0;
}
}
}
if (flag) printf("All passed\n");
return 0;
}
注意: 十万及以上级别的数组要在函数外面开。
#include
int school[100010] = {0}; // 记录每个学校的总分, 初始化为0
int main()
{
int N, schoolID, score;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
scanf("%d%d", &schoolID, &score);
school[schoolID] += score;
}
int k = 1, max = school[1];
for (int i = 2; i < N; i++) { // 从所有学校中选出总分最高的一个
if (school[i] > max) {
max = school[i];
k = i;
}
}
printf("%d %d\n", k, max);
return 0;
}
#include
char bad[130], input[100010];
int main() {
gets(bad); gets(input);
int hash[130] = {0}, i, j;
for (i = 0; bad[i]; i++) {
if (bad[i] != '+') {
if (bad[i] >= 'A' && bad[i] <= 'Z') hash[bad[i] - 'A' + 'a'] = 1;
hash[bad[i]] = 1;
}
else if (bad[i] == '+') for (j = 'A'; j <= 'Z'; j++) hash[j] = 1;
}
for (i = 0; input[i]; i++) {
if (hash[input[i]] == 0) printf("%c", input[i]);
}
printf("\n");
return 0;
}
注意:题目给的整数都在int范围,但是int*int就到达long long的最大范围了,这里要用long long才不会答案错误。
#include
#include
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { //求a与b的最大公约数
return !b ? a : gcd(b, a % b);
}
struct Fraction { //最简洁的写法, 假分数
ll up, down; //分子分母
} a, b;
Fraction reduction(Fraction result) { //化简
if (result.down < 0) { //分母为负数, 分子分母都变为相反数
result.up = -result.up;
result.down = - result.down;
}
if (result.up == 0) { //如果分子为0
result.down = 1; //分母为1
} else { //如果分子不为0, 约分
int d = gcd(abs(result.up), abs(result.down)); //分子分母的最大公约数
result.up /= d; //约去最大公约数
result.down /= d;
}
return result;
}
Fraction add(Fraction f1, Fraction f2) { //f1+f2
Fraction result;
result.up = f1.up * f2.down + f2.up * f1.down; //分数和的分子
result.down = f1.down * f2.down; //分数和的分母
return reduction(result);
}
Fraction minu(Fraction f1, Fraction f2) { //f1+f2
Fraction result;
result.up = f1.up * f2.down - f2.up * f1.down; //分数差的分子
result.down = f1.down * f2.down; //分数差的分母
return reduction(result);
}
Fraction multi(Fraction f1, Fraction f2) { //f1*f2
Fraction result;
result.up = f1.up * f2.up; //分数乘的分子
result.down = f1.down * f2.down; //分数乘的分母
return reduction(result);
}
Fraction divide(Fraction f1, Fraction f2) { //f1/f2
Fraction result;
result.up = f1.up * f2.down; //分数除的分子
result.down = f1.down * f2.up; //分数除的分母
return reduction(result);
}
void showResult(Fraction r) {
r = reduction(r);
if (r.up < 0) printf("("); //为负数则须加括号
if (r.down == 1) printf("%lld", r.up); //整数
else if (abs(r.up) > r.down) { //假分数
printf("%lld %lld/%lld", r.up / r.down, abs(r.up) % r.down, r.down);
} else { //真分数
printf("%lld/%lld", r.up, r.down);
}
if (r.up < 0) printf(")");
}
int main() {
scanf("%lld/%lld %lld/%lld", &a.up, &a.down, &b.up, &b.down);
//加法
showResult(a); printf(" + "); showResult(b); printf(" = ");
showResult(add(a, b)); printf("\n");
//减法
showResult(a); printf(" - "); showResult(b); printf(" = ");
showResult(minu(a, b)); printf("\n");
//乘法
showResult(a); printf(" * "); showResult(b); printf(" = ");
showResult(multi(a, b)); printf("\n");
//除法
showResult(a); printf(" / "); showResult(b); printf(" = ");
if (b.up == 0) printf("Inf"); //若除法分母为0,则输出Inf
else showResult(divide(a, b));
printf("\n");
return 0;
}
有陷阱,中间序列可能和初始序列一样,但是它们不是同一轮迭代的序列,初始序列不应该与中间序列比较,不然有一个数据会产生双解。
//input
4
3 4 2 1
3 4 2 1
//output
Insertion Sort
2 3 4 1
这里的中间序列是插入排序在经过第一轮迭代后的结果,虽然和目标序列一样,因此要输出第二轮插入后的结果。
另外,由于数据范围较小,归并排序可以不用合并函数而用sort替代。
#include
#include
using namespace std;
const int N = 111;
int origin[N], tempOri[N], target[N]; //原始数组 原始数组备份 目标数组
int n; //元素个数
bool isSame(int a[], int b[]) {
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) return false;
}
return true;
}
void traceArray(int a[]) { //输出数组
for (int i = 0; i < n; i++) {
printf("%d", a[i]);
if (i < n - 1) printf(" ");
}
printf("\n");
}
bool insertSort() { //插入排序
bool f = false; //记录是否在数组的中间步骤与目标数组相同
for (int i = 1; i < n; i++) {
if (i != 1 && isSame(tempOri, target)) {
f = true; //中间步骤与目标相同, 且初始序列不参与与目标数组的比较
}
int t = tempOri[i], j;
for (j = i; j > 0 && tempOri[j - 1] > t; j--)
tempOri[j] = tempOri[j - 1];
tempOri[j] = t;
if (f) { //已到达目标数组的下一次排序
return true;
}
}
return false; //无法到达目标数组
}
void mergeSort() { //归并排序
bool f = false;
for (int step = 2; step / 2 <= n; step *= 2) {
if (step != 2 && isSame(tempOri, target)) {
f = true; //中间步骤与目标相同, 且初始序列不参与与目标数组的比较
}
for (int i = 0; i < n; i += step) {
sort(tempOri + i, tempOri + min(i + step, n));
}
if (f) { //已到达目标数组的下一次排序
traceArray(tempOri);
return;
}
}
return; //无法到达目标数组
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &origin[i]); //输入初识序列
tempOri[i] = origin[i]; //备份, 排序在tempOri上面进行
}
for (int i = 0; i < n; i++) {
scanf("%d", &target[i]); //目标数组
}
if (insertSort()) { //如果插入排序中找到目标数组
printf("Insertion Sort\n");
traceArray(tempOri);
} else { //到达此处时一定时归并排序
printf("Merge Sort\n");
for (int i = 0; i < n; i++) {
tempOri[i] = origin[i]; //还原备份数组
}
mergeSort(); //归并排序
}
}
这类画图题目的做法通常有两类:
#include
int main()
{
int col;
char c;
scanf("%d %c", &col, &c);
//四舍五入 由于除以2, 通过判断奇偶数以避免浮点舍入
int row = col % 2 ? col / 2 + 1 : col / 2;
//第1行
for (int i = 0; i < col; i++)
printf("%c", c);
printf("\n");
//第2至row-1行
for (int i = 2; i < row; i++) {
printf("%c", c);
for (int j = 0; j < col - 2; j++)
printf(" ");
printf("%c\n", c);
}
//第row行
for (int i = 0; i < col; i++)
printf("%c", c);
printf("\n");
}
这种题目和时分秒转换的方法一样,但和日期转换的方法不太一样,毕竟日期有平年闰年大月小月。
#include
int main() {
int galleon = 17 * 29, sickle = 29;
int g1, s1, k1, g2, s2, k2;
scanf("%d.%d.%d %d.%d.%d", &g1, &s1, &k1, &g2, &s2, &k2);
int price = g1 * galleon + s1 * sickle + k1, money = g2 * galleon + s2 * sickle + k2;
int change = money - price;
if (change < 0) {
printf("-");
change = -change;
}
printf("%d.%d.%d\n", change / galleon, change % galleon / sickle, change % sickle);
return 0;
}
#include
int hashTable[105] = {0};
int main() {
int N, t, K;
scanf("%d", &N);
for (int i = 0; i < N; i++) {
scanf("%d", &t);
hashTable[t]++;
}
scanf("%d", &K);
for (int i = 0; i < K; i++) {
scanf("%d", &t);
if (i > 0) printf(" ");
printf("%d", hashTable[t]);
}
printf("\n");
return 0;
}
奇了怪了,为甚麽这里写成else if (neg) printf("Yes %d\n", -neg);
就不行,就有一条通不过呢?而写成else
就可以通过?
#include
int main() {
char buy[1010], want[1010];
int hash[130] = {0};
scanf("%s", buy); scanf("%s", want);
for (int i = 0; want[i]; i++) hash[want[i]]++;
for (int i = 0; buy[i]; i++) hash[buy[i]]--;
int pos = 0, neg = 0; //如果哈希表全部<=0, Yes, 负数之和即多的珠子
for (int i = 0; i < 128; i++) {
if (hash[i] > 0) pos += hash[i];
else if (hash[i] < 0) neg += hash[i];
}
if (pos) printf("No %d\n", pos); //只要存在pos, 说明缺少想要的珠子
else printf("Yes %d\n", -neg); //strlen(buy) - strlen(want)
return 0;
}
字符串APPAPT
中包含了两个单词PAT
,其中第一个PAT
是第2位§,第4位(A),第6位(T);第二个PAT
是第3位§,第4位(A),第6位(T)。现给定字符串,问一共可以形成多少个PAT
?
APPAPT APPAPTT
2 4
对一类涉及序列的题目来说可以活用递推关系,如果序列的每一位所需要的值/进行的判断,都可以通过该位左右两侧的结果计算得到,就可以考虑所谓“左右两侧的结果”是否可以通过递推进行预处理得到。这也是一种用空间换时间的方式之一。
另外,每次计算记得取模。
#include
char s[100010];
int leftPNum[100010] = {0}, rightTNum = 0; //每一位左位含P的个数; 每一位右位含T的个数
int main() {
scanf("%s", s);
int i, j;
leftPNum[0] = s[0] == 'P' ? 1 : 0;
for (i = 1; s[i]; i++) {
if (s[i] == 'P') leftPNum[i] = leftPNum[i - 1] + 1;
else leftPNum[i] = leftPNum[i - 1];
}
int ans = 0; //答案
for (j = i - 1; j >= 0; j--) {
if (s[j] == 'T') rightTNum++; //当前位是T, 右边T的个数加一
else if (s[j] == 'A') ans = (ans + leftPNum[j] * rightTNum) % 1000000007;;
}
printf("%d\n", ans);
return 0;
}
准考证号由14位数字组成,因此可以使用long long来存放它。
这里直接把试机座位号作为数组的下标,可以直接通过试机座位号来获取考生的准考证号和考试座位号,在使用记录少、空间足够的情况下,时间效率相当高。这就是哈希表的雏形了。
#include
typedef struct student {
long long id; //准考证号
int examSeat; //考试座位号
} student;
student stu[1010];
int main()
{
int N, M;
scanf("%d", &N); //考生人数
long long id;
int seat, examSeat;
for (int i = 0; i < N; i++) {
scanf("%lld %d %d", &id, &seat, &examSeat); //准考证号 试机座位号 考试座位号
stu[seat].id = id; //试机座位号为seat的考生的准考证号
stu[seat].examSeat = examSeat; //试机座位号为seat的考生的考试座位号
}
scanf("%d", &M); //查询个数
for (int i = 0; i < M; i++) {
scanf("%d", &seat);
printf("%lld %d\n", stu[seat].id, stu[seat].examSeat);
}
return 0;
}
只统计英文字符,不区分大小写,空格、’&'这些字符不应该被统计。
#include
int hash[30] = {0}, max = 0;
int change(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
}
int main() {
char s[1000];
while (scanf("%s", s) != EOF) {
for (int i = 0; s[i]; i++) {
if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z')) {
int id = change(s[i]);
hash[id]++;
}
}
}
for (int i = 0; i < 26; i++) if (hash[i] > hash[max]) max = i;
printf("%c %d\n", max + 'a', hash[max]);
return 0;
}
为了避免缓冲区溢出,从终端读取输入时应当用fgets()代替gets()函数。但是这也将带来一个问题,因为fgets()的调用格式是:
fgets (buf, MAX, fp)
fgets (buf, MAX, stdin)
buf是一个char数组的名称,MAX是字符串的最大长度,fp是FILE指针。
fgets()函数读取到它所遇到的第一个换行符的后面,或者读取比字符串的最大长度少一个的字符,或者读取到文件结尾。然后fgets()函数向末尾添加一个空字符以构成一个字符串。
如果在达到字符最大数目之前读完一行,它将在字符串的‘\0’字符之前添加一个换行符。问题出在有时字符串的结尾处可能多出一个换行符,我们可能需要把它去掉
。
#include
int hash[30] = {0}, max = 0;
int change(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
}
int main() {
char s[1010];
fgets(s, 1010, stdin);
for (int i = 0; s[i]; i++) {
if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z')) {
int id = change(s[i]);
hash[id]++;
}
}
for (int i = 0; i < 26; i++) if (hash[i] > hash[max]) max = i;
printf("%c %d\n", max + 'a', hash[max]);
return 0;
}
并非直接哈希,而是限制了一个范围(0-5),手动加上了一个映射和字典。
#include
char s[10010];
int hash[6] = {0}, sum = 0; //记录PATest这六个字符的个数; 需要输出的总字符数
char dict[] = {'P', 'A', 'T', 'e', 's', 't'}; //字典, 输出时用到
int change(char c) {
int t = -1;
switch (c) {
case 'P': t = 0; break;
case 'A': t = 1; break;
case 'T': t = 2; break;
case 'e': t = 3; break;
case 's': t = 4; break;
case 't': t = 5; break;
}
return t;
}
int main() {
fgets(s, 10010, stdin);
for (int i = 0; s[i]; i++) {
int t = change(s[i]);
if (t != -1) { hash[t]++; sum++; }
}
while (sum) {
for (int i = 0; i < 6; i++) {
if (hash[i] > 0) {
printf("%c", dict[i]);
hash[i]--; sum--;
}
}
}
return 0;
}
火星人是以 13 进制计数的:
地球人的 0 被火星人称为 tret。
地球人数字 1 到 12 的火星文分别为:jan, feb, mar, apr, may, jun,
jly, aug, sep, oct, nov, dec。
火星人将进位以后的 12 个高位数字分别称为:tam, hel, maa, huh,
tou, kes, hei, elo, syy, lok, mer, jou。
例如地球人的数字 29 翻译成火星文就是 hel mar;而火星文 elo nov 对应地球数字 115。为了方便交流,请你编写程序实现地球和火星数字之间的互译。
4
29
5
elo nov
tam
hel mar
may
115
13
如果对输入进行模拟会相对复杂,考虑到数据范围小,不如先把所有可能的结果得到,再直接查询。需要注意的是:13的倍数不应该输出个位的"tret",而是应该直接输出高位数字。
#include
#include
#include
#include
using namespace std;
string unitDigit[13] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"};
string tenDigit[13] = {"tret", "tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"};
map<string, int> strToNum; //火星文转数字
string numTostr[170]; //数字转火星文
void init() {
for (int i = 0; i < 13; i++) {
numTostr[i] = unitDigit[i]; //个位为[0, 12], 十位为0
strToNum[unitDigit[i]] = i;
numTostr[13 * i] = tenDigit[i]; //十位为[0, 12], 个位为0
strToNum[tenDigit[i]] = i * 13;
}
for (int i = 1; i < 13; i++) { //十位
for (int j = 1; j < 13; j++) { //个位
string str = tenDigit[i] + " " + unitDigit[j]; //火星文
numTostr[i * 13 + j] = str; //数字转火星文
strToNum[str] = i * 13 + j; //火星文转为数字
}
}
}
int main() {
init(); //打表
int T; scanf("%d", &T); //查询个数
getchar();
while (T--) {
string str;
getline(cin, str); //查询的数
if (str[0] >= '0' && str[0] <= '9') { //如果是数字
int num = 0; //字符串转为数字
for (int i = 0; i < str.length(); i++) {
num = num * 10 + (str[i] - '0');
}
cout << numTostr[num] << endl; //直接查表
} else { //如果是火星文
cout << strToNum[str] << endl; //直接查表
}
}
return 0;
}
著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?
例如给定 N = 5 N = 5 N=5, 排列是1、3、2、4、5。则:
1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
类似原因,4 和 5 都可能是主元。
因此,有3个元素可能是主元。
5
1 3 2 4 5
3
1 4 5
部分正确 18
,采用双指针,测试点1、3、5运行超时。
#include
int main() {
int n;
scanf("%d", &n);
int a[n], pivot[n], pNum = 0;
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
for (int i = 0; i < n; i++) {
int low = 0, high = n - 1;
if (i == 0) {
while (a[high] > a[low]) high--;
} else if (i == n - 1) {
while (a[low] < a[high]) low++;
} else {
while (a[low] < a[i]) low++;
while (a[high] > a[i]) high--;
}
if (high == low) pivot[pNum++] = a[low]; //相等时为主元
}
printf("%d\n", pNum);
for (int i = 0; i < pNum; i++) {
if (i > 0) printf(" ");
printf("%d", pivot[i]);
}
printf("\n");
return 0;
}
看了下题解,本题和B1040/A1093/A1101很像,思维类似,而和快排没有任何关系。
考虑大小的继承关系,序列为A,元素各不相同但都是正整数。令数组leftMax[i]
记录A[i]左边的最大值(从A[0]-A[i-1]
,不包括A[i]本身);令数rightMin[i]
记录A[i]右边的最小值(从A[i+1]-A[n-1]
,不包括A[i]本身),那么,如果A[i]大于左边的最大值leftMax[i]并且A[i]小于右边的最小值rightMin[i],说明左边的所有数都比它大,右边的所有数都比他小,A[i]为主元。
可以用O(n)的时间求出这两个数组,方法很简单:leftMax[i-1]记录了从A[0]到A[i-2]的最大值,leftMax[i] = A[i-1] > leftMax[i-1] ? A[i-1] : leftMax[i-1]
;同理可求出rightMin[i]。
#include
#include
using namespace std;
const int maxn = 100010;
const int INF = 0x3fffffff; //10^10左右, 一个很大的数
int a[maxn], leftMax[maxn], rightMin[maxn]; //大于左边最大, 小于右边最小, 即为主元
int main() {
int n;
scanf("%d", &n);
int pivot[n], pNum = 0; //记录主元和个数
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
leftMax[0] = 0; //a[0]的左边没有更大的数
for (int i = 1; i < n; i++) leftMax[i] = max(leftMax[i - 1], a[i - 1]);
rightMin[n - 1] = INF; //a[n-1]的右边没有更小的数
for (int i = n - 2; i >= 0; i--) rightMin[i] = min(rightMin[i + 1], a[i + 1]);
for (int i = 0; i < n; i++) //左边所有数比他小, 右边所有数比他大
if (a[i] > leftMax[i] && a[i] < rightMin[i]) pivot[pNum++] = a[i];
printf("%d\n", pNum);
for (int i = 0; i < pNum; i++) {
if (i > 0) printf(" ");
printf("%d", pivot[i]);
}
printf("\n"); //必须要有换行, 即使第二行没有输出主元, 个数为0
return 0;
}
#include
int main()
{
int N, failJ = 0, failY = 0; //甲乙输的次数
scanf("%d", &N);
while (N--) {
int j1, j2, y1, y2;
scanf("%d%d%d%d", &j1, &j2, &y1, &y2);
if (j2 == j1 + y1 && y2 != j1 + y1) failY++; //甲猜中乙没有
else if (y2 == j1 + y1 && j2 != j1 + y1) failJ++; //乙猜中甲没有
}
printf("%d %d\n", failJ, failY);
return 0;
}
要统计每个队伍的总分,因此直接开一个哈希数组,来记录对应的各个队伍队员分数之和。
#include
int hash[1001] = {0};
int main() {
int N;
scanf("%d", &N);
int c, p, g; //队伍编号-队员编号 成绩
while (N--) {
scanf("%d-%d %d", &c, &p, &g);
hash[c] += g;
}
int max = 1;
for (int i = 1; i < 1001; i++) if (hash[i] > hash[max]) max = i;
printf("%d %d\n", max, hash[max]);
return 0;
}
的reverse和swap函数都挺好用的;char s = {0}
。#include
#include
#include
using namespace std;
char JQK[] = {'J', 'Q', 'K'};
int main() {
char a[110], b[110], c[200];
scanf("%s%s", a, b);
int len1 = strlen(a), len2 = strlen(b), size = 0;
reverse(a, a + len1);
reverse(b, b + len2);
for (int i = 0; i < len1 || i < len2; i++) {
int d1 = i < len1 ? a[i] - '0' : 0, d2 = i < len2 ? b[i] - '0' : 0;
if (i % 2 == 0) {
int temp = (d1 + d2) % 13;
c[size++] = temp > 9 ? JQK[temp - 10] : temp + '0';
} else {
int t = (d2 - d1);
c[size++] = t >= 0 ? t + '0' : t + 10 + '0';
}
}
c[size] = '\0';
reverse(c, c + size);
printf("%s\n", c);
return 0;
}
多试几个例子找规律,第i位的出现次数为i*(n+1-i)
。
#include
int main() {
int n;
double v, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lf", &v);
ans += v * i * (n + 1 - i); //第i位的出现次数为i*(n+1-i)
}
printf("%.2lf\n", ans);
return 0;
}
本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”。所谓“螺旋矩阵”,是指从左上角第1个格子开始,按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列,满足条件:m×n 等于 N;m≥n;且 m−n 取所有可能值中的最小值。
12
37 76 20 98 76 42 53 95 60 81 58 93
98 95 93
42 37 81
53 20 76
58 60 76
和蛇形填数一模一样的。但是查了好久bug,就是没想明白问题出在哪里,后来发现,使用memset置0而非依赖初始化时={0},程序才能正常运行,真是奇怪。
#include
#include
#include
#include
using namespace std;
bool cmp(int a, int b) {
return a > b;
}
int main() {
int N, m, n; //m行n列, m>=n; m*n=N; m-n最小
scanf("%d", &N);
n = sqrt(N); //n <=sqrt(N) <=m
while (N % n) { n--; } //N可能为素数
m = N / n;
int a[N], matrix[m][n]; //待填充的都是正整数, 用0作为没有填数的标识
for (int i = 0; i < N; i++) scanf("%d", &a[i]);
sort(a, a + N, cmp); //非递增排序
memset(matrix, 0, sizeof(matrix));
int x = 0, y = 0, tot = 0;
matrix[x][y] = a[tot++];
while (tot < N) { //填完所有的符号为止
while (y + 1 < n && !matrix[x][y + 1]) matrix[x][++y] = a[tot++]; //先往填右
while (x + 1 < m && !matrix[x + 1][y]) matrix[++x][y] = a[tot++]; //再往下填
while (y - 1 >= 0 && !matrix[x][y - 1]) matrix[x][--y] = a[tot++]; //再往左填
while (x - 1 >= 0 && !matrix[x - 1][y]) matrix[--x][y] = a[tot++]; //再往上填
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (j > 0) printf(" ");
printf("%d", matrix[i][j]);
}
printf("\n");
}
return 0;
}
另一种实现:
#include
#include
#include
#include
using namespace std;
int N;
void solve(){
int a[N];
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
}
sort(a, a + N);
int n = sqrt(N), m;
while(N % n){
n--;
}
m = N / n;
int t[m][n];
for(int side = 0, k = N-1; side * 2 < n; side++){
for(int j = side; j < n-side; j++){
t[side][j] = a[k--];
}
for(int i = side + 1; i < m-side;i++){
t[i][n-1-side] = a[k--];
}
for(int j = n-2-side;j >= side; j--){
t[m-1-side][j] = a[k--];
}
if(n-1-side > side){
for(int i = m-2-side; i >= side + 1; i--){
t[i][side] = a[k--];
}
}
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
printf("%d", t[i][j]);
if(j + 1< n){
printf(" ");
}
}
printf("\n");
}
}
int main(){
scanf("%d", &N);
solve();
return 0;
}
这里涉及到浮点数的比较问题,有点麻烦。
fabs(real)
这两句话是不可少的,否则会有-0.00的出现。e.g: -0.001,保留两位小数,变成-0.00,这就会导致有两个案例wrong answer。(int)imag < 0
,这会导致-0.1这类的虚部输出错误,可以直接写成imag < 0
(0隐式转为浮点数)或者imag < 0.0
。这在简单的比较情况下是可以忽略误差的,但是最好的做法还是如下所示。#include
#include
const double eps = 1e-8;
#define Less(a, b) (((a) - (b)) < (-eps))
int main() {
double r1, p1, r2, p2;
scanf("%lf%lf%lf%lf", &r1, &p1, &r2, &p2);
double b = r1 * r2;
double real = b * (cos(p1) * cos(p2) - sin(p1) * sin(p2));
double imag = b * (cos(p1) * sin(p2) + sin(p1) * cos(p2));
if (fabs(real) < 0.005) real = 0;
if (fabs(imag) < 0.005) imag = 0;
if (Less(imag, 0)) printf("%.2lf%.2lfi\n", real, imag);
else printf("%.2lf+%.2lfi\n", real, imag);
return 0;
}
注意:
#include
#include
#include
#include
using namespace std;
int main() {
map<int, string> emoji[3]; //手、眼、口的可选符号集及其序号
for (int c = 0; c < 3; c++) {
string use;
getline(cin, use);
int k = 1;
for (int i = 0; i < use.length(); ) {
string t1 = "";
if (use[i] == '[') {
while (use[++i] != ']') t1 += use[i];
//cout << t1 << endl;
emoji[c][k++] = t1; //符号序号与符号映射
i++; //移到下一个[]
} else i++;
}
}
int n, t[5], index; scanf("%d", &n);
while (n--) {
int flag = 1;
for (int i = 0; i < 5; i++) {
scanf("%d", &t[i]);
index = i > 2 ? 5 - i - 1 : i;
if (t[i] > emoji[index].size() || t[i] < 1) {
flag = 0;
}
}
if (flag) {
for (int i = 0; i < 5; i++) {
index = i > 2 ? 5 - i - 1 : i;
cout << emoji[index][t[i]];
if (i == 0) cout << "("; //打印左脸颊
else if (i == 3) cout << ")"; //打印右脸颊
}
cout << endl;
} else {
printf("Are you kidding me? @\\/@\n");
}
}
return 0;
}
输出%要转义为%%。
#include
const double eps = 1e-8;
#define Less(a, b) (((a) - (b)) < -eps)
int main() {
int n, d; //居民区住房总套数 观察期阈值
double e; //低电量阈值
scanf("%d%lf%d", &n, &e, &d);
int probEmpty = 0, empty = 0; //可能空置户数 空置户数
for (int i = 0; i < n; i++) {
int k, lowEday = 0; //低于某给定的阈值e天数
double t;
scanf("%d", &k);
for (int j = 0; j < k; j++) {
scanf("%lf", &t);
if (Less(t, e)) lowEday++;
}
if (2 * lowEday > k) { //超过一半的日子用电量低于某给定的阈值e
if (k > d) empty++; //超过某给定阈值D天数, 为空置
else probEmpty++;
}
}
printf("%.1lf%% %.1lf%%\n", probEmpty * 100 / (double)n, empty * 100 / (double)n);
return 0;
}
本题的基本要求非常简单:给定 N 个实数,计算它们的平均值。但复杂的是有些输入数据可能是非法的。一个“合法”的输入是 [−1000,1000] 区间内的实数,并且最多精确到小数点后 2 位。当你计算平均值的时候,不能把那些非法的数据算在内。
7
5 -3.2 aaa 9999 2.3.4 7.123 2.35
ERROR: aaa is not a legal number
ERROR: 9999 is not a legal number
ERROR: 2.3.4 is not a legal number
ERROR: 7.123 is not a legal number
The average of 3 numbers is 1.38
2
aaa -9999
ERROR: aaa is not a legal number
ERROR: -9999 is not a legal number
The average of 0 numbers is Undefined
如果使用sscanf和sprintf函数,这题会很简单。
sscanf() – 从一个字符串中读进与指定格式相符的数据
sprintf() – 字符串格式化命令,主要功能是把格式化的数据写入某个字符串中。
如果(ˇˍˇ)想知道效果,可以添加一行打印语句。
//input
7
5 -3.2 aaa 9999 2.3.4 7.123 2.35
//output
5 = 5 = 5.00
-3.2 = -3.2 = -3.20
aaa = -3.2 = -3.20
ERROR: aaa is not a legal number
9999 = 9999 = 9999.00
ERROR: 9999 is not a legal number
2.3.4 = 2.3 = 2.30
ERROR: 2.3.4 is not a legal number
7.123 = 7.123 = 7.12
ERROR: 7.123 is not a legal number
2.35 = 2.35 = 2.35
The average of 3 numbers is 1.38
#include
#include
using namespace std;
int main() {
int N, cnt = 0;
double sum = 0.0, temp;
char a[100], b[100];
scanf("%d", &N);
while (N--) {
scanf("%s", a);
sscanf(a, "%lf", &temp); //从字符数组a中以浮点数的形式读入
//cout << a << " = " << temp << " = ";
sprintf(b, "%.2lf", temp); //将浮点数temp以小数点后两位精度的形式输出到字符数组b中
//cout << b << endl;
int flag = 0;
for (int j = 0; a[j]; j++) //以s为基准
if (a[j] != b[j]) flag = 1;
if (flag || temp > 1000 || temp < -1000) {
printf("ERROR: %s is not a legal number\n", a);
continue;
} else {
sum += temp;
cnt++;
}
}
if (cnt == 1) printf("The average of 1 number is %.2lf", sum);
else if (cnt > 0) printf("The average of %d numbers is %.2lf", cnt, sum / cnt);
else printf("The average of 0 numbers is Undefined");
return 0;
}
拍集体照时队形很重要,这里对给定的 N 个人 K 排的队形设计排队规则如下:
每排人数为 N/K(向下取整),多出来的人全部站在最后一排;
后排所有人的个子都不比前排任何人矮;
每排中最高者站中间(中间位置为 m/2+1,其中 m 为该排人数,除法向下取整);
每排其他人以中间人为轴,按身高非增序,先右后左交替入队站在中间人的两侧(例如5人身高为190、188、186、175、170,则队形为175、188、190、186、170。这里假设你面对拍照者,所以你的左边是中间人的右边);
若多人身高相同,则按名字的字典序升序排列。这里保证无重名。
现给定一组拍照人,请编写程序输出他们的队形。
10 3
Tom 188
Mike 170
Eva 168
Tim 160
Joe 190
Ann 168
Bob 175
Nick 186
Amy 160
John 159
Bob Tom Joe Nick
Ann Mike Eva
Tim Amy John
既然后排的人个子都更高,而且先输出后排,那么先按照身高降序排序,身高相同的再按名字升序排序。排队的过程就是填充vector的过程,和输出图形一样的方法。
#include
#include
#include
#include
using namespace std;
struct node {
string name;
int h; //身高
};
bool cmp(const node &a, const node &b) { //后排的人输出在上方,前排输出在下方
return a.h != b.h ? a.h > b.h : a.name < b.name; //先取高的后排
}
int main() {
int n, k, m; //总人数 K排 每排人数
cin >> n >> k;
vector<node> queue(n);
for (int i = 0; i < n; i++) {
cin >> queue[i].name >> queue[i].h;
}
sort(queue.begin(), queue.end(), cmp);
int row = k, t = 0; //k排
while (row) {
if (row == k) m = n - n / k * (k - 1); //最后一排的人数
else m = n / k;
vector<string> ans(m);
ans[m / 2] = queue[t].name; //第一个高的
// 左边一列
int j = m / 2 - 1;
for (int i = t + 1; i < t + m; i = i + 2) {
ans[j--] = queue[i].name;
}
//右边一列
j = m / 2 + 1;
for (int i = t + 2; i < t + m; i = i + 2) {
ans[j++] = queue[i].name;
}
//输出当前列
cout << ans[0];
for (int i = 1; i < m; i++) cout << " " << ans[i];
cout << endl;
t = t + m;
row--;
}
return 0;
}
组合的数字的可能太多了,为O((n-1)n)。
#include
int combine(int a, int b) { //a十位 b个位
return a * 10 + b;
}
int main() {
int n;
scanf("%d", &n);
int a[n];
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int sum = 0;
for (int i = 0; i < n; i++) {
int t = a[i];
for (int j = 0; j < n; j++) {
if (a[j] != t) sum += combine(t, a[j]);
}
}
printf("%d\n", sum);
return 0;
}
部分正确 19
,这里使用了特殊的存入一行字符串的代码;另外使用hash计算sum二进制表示中0、1的个数。
#include
char s[100010];
int main() {
scanf("%[^\n]", s); //不存换行符
int sum = 0;
for (int i = 0; s[i]; i++) {
if (s[i] >= 'A' && s[i] <= 'Z') sum += s[i] - 'A' + 1;
else if (s[i] >= 'a' && s[i] <= 'z') sum += s[i] - 'a' + 1;
}
int hash[2] = {0};
do {
hash[sum % 2]++;
sum /= 2;
} while (sum != 0);
printf("%d %d\n", hash[0], hash[1]);
return 0;
}
正确代码:使用了ctype.h中的字符测试函数;当sum为0的时候,说明该字符串中没有任何英文字母,应该特判为"0 0"。
#include
#include
char s[100010];
int main() {
scanf("%[^\n]", s);
int sum = 0;
for (int i = 0; s[i]; i++) {
if (isupper(s[i])) sum += s[i] - 'A' + 1;
else if (islower(s[i])) sum += s[i] - 'a' + 1;
}
if (sum == 0) printf("0 0\n");
else {
int hash[2] = {0};
do {
hash[sum % 2]++;
sum /= 2;
} while (sum != 0);
printf("%d %d\n", hash[0], hash[1]);
}
return 0;
}
自己这么写字符数组太麻烦了。这里比较学生的答案对不对,是将学生的答案选项(字典序)连接成字符串,与正确答案(字典序)字符串比较。这个题目的难题版本在B1073 多选题常见计分法
。
#include
#include
struct choice {
int score;
char right[10];
} answer[101];
struct student {
int total;
char ch[10];
};
int error[101] = {0}, max = 0; //每道题的错误次数
int main() {
int n, m; scanf("%d%d", &n, &m); //N ≤1000和M ≤100
int num, rightNum; //选项个数 正确选项个数
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &answer[i].score, &num, &rightNum);
int j;
for (j = 0; j < rightNum; j++) {
getchar();
scanf("%c", &answer[i].right[j]);
}
answer[i].right[j] = '\0';
}
getchar(); //吸收换行符
struct student t;
for (int i = 1; i <= n; i++) {
t.total = 0;
for (int k = 1; k <= m; k++) { //题目顺序
scanf("(%d", &num);
int j;
for (j = 0; j < num; j++) {
getchar();
scanf("%c", &t.ch[j]);
}
t.ch[j] = '\0';
getchar(); getchar(); //吸收后括号和空格/换行符
if (strcmp(t.ch, answer[k].right) == 0) t.total += answer[k].score;
else error[k]++; //错误次数加1
}
printf("%d\n", t.total);
}
for (int i = 0; i <= 100; i++) {
if (error[i] > max) max = error[i];
}
if (max == 0) printf("Too simple\n"); //所有题目都没有人错
else {
printf("%d", max);
for (int i = 0; i <= 100; i++) {
if (error[i] == max) printf(" %d", i);
}
}
return 0;
}
速度比较慢,但是都能通过:
#include
const int maxn = 10100;
bool p[maxn] = {false};
void findPrime(int n) {
for (int i = 2; i < maxn; i++) {
if (i > n) break;
if (p[i] == false) {
for (int j = i + i; j < maxn; j += i) {
p[j] = true;
}
}
}
}
char award[][25] = {"Mystery Award", "Minion", "Chocolate"};
struct person {
int id;
int type;
bool checked;
person() { checked = false;}
} a[maxn];
int main() {
int n;
scanf("%d", &n);
findPrime(n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].id);
if (i == 1) a[i].type = 0;
else if (p[i] == false) a[i].type = 1; //排名为素数的学生将赢得黄人玩偶
else a[i].type = 2;
}
int k, t;
scanf("%d", &k);
for (int i = 0; i < k; i++) {
scanf("%d", &t);
int flag = 1, j = 1;
while (j <= n && a[j].id != t) j++;
if (j > n) {
printf("%04d: Are you kidding?\n", t);
continue;
}
if (a[j].checked == false) {
printf("%04d: %s\n", t, award[a[j].type]);
a[j].checked = true;
} else printf("%04d: Checked\n", t);
}
return 0;
}
英国天文学家爱丁顿很喜欢骑车。据说他为了炫耀自己的骑车功力,还定义了一个“爱丁顿数” E ,即满足有E天骑车超过E英里的最大整数E。据说爱丁顿自己的E等于87。
现给定某人N天的骑车距离,请你算出对应的爱丁顿数E(≤N)。
10
6 7 6 9 3 10 8 2 7 8
6
这题写起来简单,但是想通就有点难。
#include
#include
using namespace std;
const int maxn = 100000;
int a[maxn];
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
sort(a, a + n);
int E = 0;
for (int i = 0; i < n; i++) {
if (a[i] > n - i) { //a[i]超过从n-i(a[i]到最后的项数), 即满足有n-i天骑车超过n-1英里
E = n - i;
break;
}
}
printf("%d\n", E);
return 0;
}
#include
int main() {
int n, m;
scanf("%d%d", &n, &m);
int fullscore[m], right[m];
for (int i = 0; i < m; i++) scanf("%d", &fullscore[i]);
for (int i = 0; i < m; i++) scanf("%d", &right[i]);
for (int i = 0; i < n; i++) {
int sum = 0, t;
for (int j = 0; j < m; j++) {
scanf("%d", &t);
sum += t == right[j] ? fullscore[j] : 0;
}
printf("%d\n", sum);
}
return 0;
}
一个分数一般写成两个整数相除的形式:N/M,其中 M 不为0。最简分数是指分子和分母没有公约数的分数表示形式。现给定两个不相等的正分数 N1/M1 和 N2/M2
,要求你按从小到大的顺序列出它们之间分母为 K 的最简分数
。
7/18 13/20 12
5/12 7/12
关键点在于第一个分数不一定小于第二个分数,这个时候就该调换两个给定分数,以便从小数到大。不然会有一个测试点错误。下面这个方法是为了熟悉分数类的写法,但是通过最后一个测试点时,耗时长。
#include
#include
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { //求a与b的最大公约数
return !b ? a : gcd(b, a % b);
}
ll lcm(ll a, ll b) { //求a与b的最小公倍数
return a / gcd(a, b) * b;
}
struct Fraction { //最简洁的写法, 假分数
ll up, down; //分子分母
} a, b;
Fraction reduction(Fraction result) { //化简, 仅仅针对正分子
//如果分子不为0, 约分
int d = gcd(abs(result.up), abs(result.down)); //分子分母的最大公约数
result.up /= d; //约去最大公约数
result.down /= d;
return result;
}
int main() {
ll K; //N/M的格式给出两个正分数 正整数分母K
scanf("%lld/%lld %lld/%lld %lld", &a.up, &a.down, &b.up, &b.down, &K);
if (a.up * b.down > b.up * a.down) swap(a, b); //a>b时调换
ll threelcm = lcm(lcm(a.down, b.down), K); //三分母最小公倍数, 用来通分
Fraction t1, t2;
int len = 0;
a.up *= (threelcm / a.down);
b.up *= (threelcm / b.down);
a.down = b.down = t1.down = threelcm; //通分
int flag = false;
for (ll i = a.up + 1; i < b.up; i++) {
t1.up = i; //枚举每一个可能的分子
t2 = reduction(t1); //化简
if (t2.down == K) { //是分母为K的最简分数
printf("%s%lld/%lld", flag ? " " : "", t2.up, t2.down); //输出最简分数
flag = true;
}
}
return 0;
}
第二个方法如下:n1/m1,n2/m2。因为要列出n1/m1和n2/m2之间的最简分数,但是n1/m1不一定小于n2/m2,所以如果n1 * m2 > n2 * m1,说明n1/m1比n2/m2大,则调换n1和n2、m1和m2的位置
。
假设所求的分数为up/k,先令up=1,当n1 * k >= m1 * up
时,up不断++,直到up符合n1/m1 < up/k为止
。然后在n1/m1和n2/m2之间找符合条件的up的值
,用gcd(up, k)是否等于1判断up和k是否有最大公约数,如果等于1表示没有最大公约数,即此时为最简,就输出up/k,然后up不断++直到退出循环。
#include
using namespace std;
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);
}
int main() {
int n1, m1, n2, m2, k;
scanf("%d/%d %d/%d %d", &n1, &m1, &n2, &m2, &k);
if(n1 * m2 > n2 * m1) { //a>b时调换
swap(n1, n2); //没有分数类, 所以分子和分母分别调换
swap(m1, m2);
}
int up = 1;
bool flag = false;
while (n1 * k >= m1 * up) up++;
while (n1 * k < m1 * up && m2 * up < n2 * k) { //分数间比较大小
if(gcd(up, k) == 1) {
printf("%s%d/%d", flag == true ? " " : "", up, k);
flag = true;
}
up++;
}
return 0;
}
这里本想使用round函数的,但是全部错误,发现我对这几个函数的使用不太清楚:
double floor(double)
:返回不大于x的最大整数值(返回为double型),向下取整;double ceil(double)
:返回不小于x的最小整数值(返回为double型),向上取整;double round(double)
:返回x的四舍五入整数值(返回为double型,其实就是一个整数值)。等价于:double round(double x) {
return x > 0.0 ? florr(x + 0.5) : ceil(x - 0.5);
}
#include
#include
int main() {
int n;
scanf("%d", &n);
int real, imag;
double max = 0.0;
while (n--) {
scanf("%d%d", &real, &imag);
double t = sqrt(1.0 * real * real + 1.0 * imag * imag);
if (t > max) max = t;
}
printf("%.2lf", max);
return 0;
}
简单整值映射。
#include
const int maxn = 10010;
int hash[maxn] = {0};
int main() {
int n;
scanf("%d", &n);
int t, friendidNum = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &t);
int friendid = 0;
while (t) {
friendid += t % 10;
t /= 10;
}
if (hash[friendid] == 0) friendidNum++;
hash[friendid]++;
}
printf("%d\n", friendidNum);
for (int i = 0; i < maxn; i++) {
if (hash[i] != 0) {
printf("%d", i); //输出朋友证号
friendidNum--;
if (friendidNum > 0) printf(" ");
}
}
printf("\n");
return 0;
}
部分正确 21
,测试点3答案错误。后来发现ID要是五位数的(从00000-99999),我忘记把没有五位的数字输出为(0填充)五位了。
#include
#include
#include
using namespace std;
const int maxn = 100100;
int cp[maxn]; //00000到99999
int main() {
memset(cp, -1, sizeof(cp));
int n, p1, p2;
scanf("%d", &n);
while (n--) {
scanf("%d%d", &p1, &p2);
cp[p1] = p2;
cp[p2] = p1;
}
int m; scanf("%d", &m); //≤10000
int people[m];
for (int i = 0; i < m; i++){
scanf("%d", &people[i]);
}
int sinNum = 0, sinPerson[m + 10];
for (int i = 0; i < m; i++) {
if (cp[people[i]] == -1) sinPerson[sinNum++] = people[i];
//伴侣对中没有这个人, 说明是单身狗
else {
int t = cp[people[i]], j = 0; //有伴侣找找伴侣来了没有
while (j < m && people[j] != t) j++;
if (j >= m) sinPerson[sinNum++] = people[i]; //他的伴侣没来, 落单
}
}
sort(sinPerson, sinPerson + sinNum);
printf("%d\n", sinNum);
for (int i = 0; i < sinNum; i++) {
if (i > 0) printf(" ");
printf("%05d", sinPerson[i]);
}
// printf("\n");
return 0;
}
#include
int main() {
int m, n, a, b, grey;
scanf("%d%d%d%d%d", &m, &n, &a, &b, &grey);
int data[n];
while (m--) {
for (int i = 0; i < n; i++) scanf("%d", &data[i]);
for (int i = 0; i < n; i++) {
if (i > 0) printf(" ");
if (data[i] >= a && data[i] <= b) printf("%03d", grey);
else printf("%03d", data[i]);
}
printf("\n");
}
return 0;
}
部分正确 19
:坑的是:要getchar();以后要用getline(我已经不想再用fgets了……几次都坑在这里),这里的问题比较隐蔽,当人们输入密码没有满次数又不想输了,“#”直接退出,而这里我使用字符串比较函数,其实是将输入的"#\n"与”#“比较(没有及时处理fgets输入的字符串末尾的换行符),因此不会退出,而是输出错误提示?。
总结一下目前已知的合理输入一行的方法:
fgets(char [], maxSize, stdin)
,文件末尾输入结束返回NULL,自己处理末尾多出来的换行符;cin.getline(char [], maxSize)
;getline(cin, string)
;scanf("%[^\n]", char [])
,相关链接:https://www.cnblogs.com/orange1438/archive/2013/05/12/4544958.html,讲得很清楚了。#include
#include
int main() {
int n, cnt = 0;
char password[22], tryPass[100];
scanf("%s%d", password, &n);
getchar(); //吸收掉换行符
while (fgets(tryPass, 100, stdin) != NULL) {
if (strcmp(tryPass, "#") == 0) break;
if (cnt++ == n) {
printf("Account locked\n");
break;
}
int len = strlen(tryPass);
if (tryPass[len - 1] == '\n') tryPass[len - 1] = '\0';
if (strcmp(password, tryPass) == 0) {
printf("Welcome in\n");
break;
} else printf("Wrong password: %s\n", tryPass);
}
return 0;
}
正确代码:
#include
#include
int main() {
int n, cnt = 0;
char password[22], tryPass[100];
scanf("%s%d", password, &n);
getchar(); //吸收掉换行符
while (fgets(tryPass, 100, stdin) != NULL) {
if (cnt++ >= n) { puts("Account locked"); break; } //满次数直接退出
int len = strlen(tryPass);
if (tryPass[len - 1] == '\n') tryPass[len - 1] = '\0';
if (strcmp(tryPass, "#") == 0) break;
if (strcmp(password, tryPass) == 0) { puts("Welcome in"); break; }
else printf("Wrong password: %s\n", tryPass);
}
return 0;
}
对于计算机而言,颜色不过是像素点对应的一个 24 位的数值。现给定一幅分辨率为 M×N 的画,要求你找出万绿丛中的一点红,即有独一无二颜色的那个像素点,并且该点的颜色与其周围 8 个相邻像素的颜色差充分大。
8 6 200
0 0 0 0 0 0 0 0
65280 65280 65280 16711479 65280 65280 65280 65280
16711479 65280 65280 65280 16711680 65280 65280 65280
65280 65280 65280 65280 65280 65280 165280 165280
65280 65280 16777015 65280 65280 165280 65480 165280
16777215 16777215 16777215 16777215 16777215 16777215 16777215 16777215
(5, 3): 16711680
4 5 2
0 0 0 0
0 0 3 0
0 0 0 0
0 5 0 0
0 0 0 0
Not Unique
3 3 5
1 2 3
3 4 5
5 6 7
Not Exist
部分正确 17
,清楚问题所在。这里的错误思路其实是暴力检查每一个点与周围点的颜色差,记录充分大的那些点,然后统计它们的个数,没有就是不存在;如果存在且只存在一个点出现次数为1,就是答案;如果有多个点出现次数为1就Not Unique……虽然我只放了检查中间的代码上来,而且后面的统计也不是那样写的…但至少还有17分(笑)…
不过其实,这里对题意的理解也有错误,比如与其周围相邻像素的颜色差,还可能是负数,因此这种暴力膜的想法就不可取了,要添加好些绝对值函数调用……太麻烦了,不如重新写。
#include
const int maxn = 1010;
int a[maxn][maxn] = {0};
int main() {
int m, n, tol;
scanf("%d%d%d", &m, &n, &tol);
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) scanf("%d", &a[i][j]);
int x, y, color, sum = 0; //j+1 i+1
for (int i = 0; i < n; i++) { //n行
for (int j = 0; j < m; j++) { //m列
int flag = 0, t = a[i][j] - tol; //检查每一个点
if ((i != 0 && i != n - 1) && (j != 0 && j != m - 1)) { //检查中间
if (t > a[i-1][j-1] && t > a[i-1][j] && t > a[i-1][j+1] &&
t > a[i][j-1] && t > a[i][j+1] &&
t > a[i+1][j-1] && t > a[i+1][j] && t > a[i+1][j+1]) flag = 1;
}
if (flag) {
sum++; color = a[i][j];
x = j + 1, y = i + 1;
}
}
}
if (sum == 1) printf("(%d, %d): %d\n", x, y, color);
else if (sum == 0) printf("Not Exist\n");
else if (sum > 1) printf("Not Unique\n");
return 0;
}
换种角度,先找到独一无二的点,再判断是不是色差充分大。如果没有独一无二的点或者色差不够大,Not Exist;有几个,Not Unique……这里用map的key表示数字,value来表示key出现的次数。用8*2个偏移值表示不同的方向,用函数抽出共同的逻辑,这一点值得借鉴。
#include
#include
#include
using namespace std;
int n, m, tol; //M×N
int s[1001][1001];
map<int, int> vis; //八个方向
int dir[8][2] = {1,0, -1,0, 0,1, 0,-1, 1,1, 1,-1, -1,1, -1,-1};
//判断是否大于阈值
bool check(int x, int y) {
for (int i = 0; i < 8; i++) {
int xx = x + dir[i][0], yy = y + dir[i][1];
if (xx >= 0 && xx < n && yy < m && yy >= 0 &&
abs(s[xx][yy] - s[x][y]) <= tol)
return false;
}
return true;
}
int main() {
cin >> m >> n >> tol;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> s[i][j];
vis[s[i][j]]++;
}
}
//cnt记录只出现1次的数字的个数
//x y记录坐标
int cnt = 0, x, y;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (vis[s[i][j]] == 1 && check(i, j)) {
cnt++;
x = i; y = j;
}
}
}
if (cnt == 1) printf("(%d, %d): %d\n",y+1, x+1, s[x][y]);
else if (cnt > 1) puts("Not Unique");
else puts("Not Exist");
return 0;
}
微博的网友的昵称(不超过 20 个字符、不包含空格回车的非空字符串)太长,自己哈希很麻烦,而且要判断处于当前中奖位置的网友中过奖没有……所以使用了map,插入同样的键时会覆盖掉以前的记录,使用set也可以(自动去重排序)。另外,发现vector可以有多种初始化方式,包括给出初始大小,设定初值等。
#include
#include
#include
#include
#include
using namespace std;
int main() {
int m, n, s;
scanf("%d%d%d", &m, &n, &s);
map<string, int> mp;
vector<string> people(m); //m个元素的变长数组
for (int i = 0; i < m; i++) {
cin >> people[i];
mp.insert(make_pair(people[i], 0)); //没有访问过
}
int cnt = 0; //中奖人数
for (int i = s - 1; i < m; ) {
if (mp[people[i]] == 0) {
cnt++; //中奖人数+1
cout << people[i] << endl;
mp[people[i]] = 1;
i += n; //查找下一个中奖间隔的人
} else i++;
}
if (!cnt) printf("Keep going...\n");
return 0;
}
给定一段一段的绳子,你需要把它们串成一条绳。每次串连的时候,是把两段绳子对折,再如下图所示套接在一起。这样得到的绳子又被当成是另一段绳子,可以再次对折去跟另一段绳子串连。每次串连后,原来两段绳子的长度就会减半。
给定N段绳子的长度,你需要找出它们能串成的绳子的最大长度。
8
10 15 12 3 4 13 1 15
14
分析:因为所有长度都要串在一起,每次都等于(旧的绳子长度+新的绳子长度)/2,所以越是早加入绳子长度中的段,越要对折的次数多,所以既然希望绳子长度是最长的,就必须让长的段对折次数尽可能的短。所以将所有段从小到大排序,然后从头到尾从小到大分别将每一段依次加入结绳的绳子中,最后得到的结果才会是最长的结果。
#include
#include
using namespace std;
int main() {
int n;
scanf("%d", &n);
int a[n];
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
sort(a, a + n);
int max = a[0];
for (int i = 1; i < n; i++) {
max = (max + a[i]) / 2;
}
printf("%d\n", (int)max);
return 0;
}
#include
int main() {
int token, n;
scanf("%d%d", &token, &n);
//n1和n2是先后给出的两个[0, 9]内的整数, 两个数字不相等;
//b为0表示赌小, 为1表示玩家赌大; t表示下注的筹码数
int n1, b, t, n2;
while (n--) {
scanf("%d%d%d%d", &n1, &b, &t, &n2);
if (t > token) {
printf("Not enough tokens. Total = %d.\n", token);
continue;
}
if ((!b && n2 < n1) || (b && n2 > n1)) { //赌小赌赢了/赌大赌赢了
token += t;
printf("Win %d! Total = %d.\n", t, token);
continue;
} else { //赌输了
token -= t;
printf("Lose %d. Total = %d.\n", t, token);
if (token == 0) { //赌输输光
printf("Game Over.\n");
break;
}
}
}
return 0;
}
需要注意的是,如果一个学生没有任何违规物品,那么就不用输出任何信息,包括换行符。不然四个测试点都是格式错误。
#include
struct student {
char name[6];
int num, stuffs[12];
};
int n, m;
int findban(int x, int ban[]) {
for (int i = 0; i < m; i++) {
if (ban[i] == x) return 1;
}
return 0;
}
int main() {
scanf("%d%d", &n, &m);
int banStuff[m];
for (int i = 0; i < m; i++) scanf("%d", &banStuff[i]);
struct student t;
int number = 0, legalStuffs = 0; //违规学生人数/被查缴物品的总数
for (int i = 0; i < n; i++) {
scanf("%s%d", t.name, &t.num);
int cnt = 0;
for (int j = 0; j < t.num; j++) {
scanf("%d", &t.stuffs[j]);
if (findban(t.stuffs[j], banStuff) == 1) {
legalStuffs++; //被查缴物品的总数+1
if (cnt == 0) {
printf("%s: %04d", t.name, t.stuffs[j]);
number++; cnt++; //违规学生人数+1
} else printf(" %04d", t.stuffs[j]);
}
}
if (cnt) printf("\n"); //如学生没有任何违规物品,就不用输出任何信息,包括换行符
}
printf("%d %d\n", number, legalStuffs);
return 0;
}
有一种最常见的计分方法是:如果考生选择了部分正确选项,并且没有选择任何错误选项,则得到 50% 分数;如果考生选择了任何一个错误的选项,则不能得分。本题就请你写个程序帮助老师批改多选题,并且指出哪道题的哪个选项错的人最多。
3 4
3 4 2 a c
2 5 1 b
5 3 2 b c
1 5 4 a b d e
(2 a c) (3 b d e) (2 a c) (3 a b e)
(2 a c) (1 b) (2 a b) (4 a b d e)
(2 b d) (1 e) (1 c) (4 a b c d)
3.5
6.0
2.5
2 2-e
2 3-a
2 3-b
2 2
3 4 2 a c
2 5 1 b
(2 a c) (1 b)
(2 a c) (1 b)
5.0
5.0
Too simple
这是使用了位运算的版本,a-e表示为1, 2, 4, 8, 16。如ac,则表示为1+4。其实就是00000,每位选不选的意思。如果考生的答案与正确答案完全相同,则异或的结果为0;
#include
#include
#include
using namespace std;
int main() {
int n, m, optnum, truenum, temp, maxcnt = 0;
int hash[] = {1, 2, 4, 8, 16}, opt[1010][110] = {0};
char c;
scanf("%d %d", &n, &m);
vector<int> fullscore(m), trueopt(m); //记录每道题的满分和正确选项
vector<vector<int>> cnt(m, vector<int>(5)); //每道题的5个选项分别错的次数
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &fullscore[i], &optnum, &truenum);
for (int j = 0; j < truenum; j++) {
scanf(" %c", &c);
trueopt[i] += hash[c-'a']; //叠加答案选项
}
}
for (int i = 0; i < n; i++) {
double grade = 0; //记录每个学生的成绩
for (int j = 0; j < m; j++) {
getchar();
scanf("(%d", &temp);
for (int k = 0; k < temp; k++) {
scanf(" %c)", &c);
opt[i][j] += hash[c-'a'];
}
int el = opt[i][j] ^ trueopt[j];
if (el) { //不为0
if ((opt[i][j] | trueopt[j]) == trueopt[j]) { //漏选
grade += fullscore[j] * 1.0 / 2;
}
if (el) { //记录错选漏选的选项的次数
for (int k = 0; k < 5; k++)
if (el & hash[k]) cnt[j][k]++;
}
} else { //为0, 得满分
grade += fullscore[j];
}
}
printf("%.1f\n", grade);
}
for (int i = 0; i < m; i++)
for (int j = 0; j < 5; j++) //记录错得最多的选项的次数
maxcnt = maxcnt > cnt[i][j] ? maxcnt : cnt[i][j];
if (maxcnt == 0) { //记录每个学生的成绩
printf("Too simple\n");
} else {
for (int i = 0; i < m; i++) {
for (int j = 0; j < cnt[i].size(); j++) {
if (maxcnt == cnt[i][j]) //记录每个学生的成绩
printf("%d %d-%c\n", maxcnt, i+1, 'a'+j);
}
}
}
return 0;
}
N位的进制表(0 < N ≤ 20),以回车结束。 随后两行,每行给出一个不超过N位的非负的PAT数。PAT数可能越过long long的界了,保险起见使用大整数的模版,把加法操作改一下就可以了。
#include
#include
#include
using namespace std;
const int N = 25;
struct bign {
int d[N], len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char s[]) {
bign a;
a.len = strlen(s);
for (int i = 0; s[i]; i++) {
a.d[i] = s[a.len - i - 1] - '0';
}
while (a.len > 1 && a.d[a.len - 1] == 0) a.len--; //去掉多余的零
return a;
}
bign list;
bign patAdd(bign a, bign b) {
bign t;
int carry = 0;
for (int i = 0; i < a.len || i < b.len; i++) {
int r = (carry + a.d[i] + b.d[i]);
if (list.d[i] != 0) {
t.d[t.len++] = r % list.d[i];
carry = r / list.d[i];
} else {
t.d[t.len++] = r % 10;
carry = r / 10;
}
}
if (carry) t.d[t.len++] = carry;
return t;
}
bign print(bign a) {
for (int i = a.len - 1; i >= 0; i--) {
printf("%d", a.d[i]);
}
printf("\n");
}
int main() {
char s[N], a[N], b[N];
scanf("%s%s%s", s, a, b);
list = change(s);
bign A = change(a), B = change(b);
bign ans = patAdd(A, B);
print(ans);
return 0;
}
#include
int main() {
int n;
scanf("%d", &n);
int res[n], size = 0;
getchar(); ////吸收换行符
char id, ans;
while (n--) {
for (int i = 0; i < 4; i++) {
scanf("%c-%c", &id, &ans);
getchar(); //吸收空格或换行符
if (ans == 'T') res[size++] = id - 'A' + 1;
}
}
for (int i = 0; i < size; i++) printf("%d", res[i]);
printf("\n");
return 0;
}
#include
#include
#include
using namespace std;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
int a[n] = {0}, size = 0, t;
for (int j = 0; j < n; j++) {
scanf("%d", &t);
if (t >= 0 && t <= m) a[size++] = t; //筛选出合法分数(包括老师的)
}
sort(a + 1, a + size);
double sum = 0.0;
for (int k = 2; k < size - 1; k++) sum += a[k]; //排除一个最小和一个最大值
sum /= ((size - 1 - 2) * 1.0); //取平均分
printf("%d\n", (int)round((sum + a[0]) / 2));
}
return 0;
}
字符串压缩就是把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示。例如ccccc
就用5c
来表示。如果字符没有重复,就原样输出。解压方法就是反过来,把形如5c
这样的表示恢复为ccccc
。
照着题意模拟就是了,这题简单。
#include
#include
#include
int main() {
char c, s[1010];
scanf("%c", &c); getchar();
scanf("%[^\n]", s);
if (c == 'C') {
for (int i = 0; s[i]; ) {
char t = s[i];
int j = i + 1; //一个字符连续重复的次数
while (s[j] && s[j] == t) j++;
if (j - i <= 1) printf("%c", t);
else printf("%d%c", j - i, t);
i = j; //跳过相同的字符
}
}
else {
for (int i = 0; s[i]; ) {
int j = i, sum = 0;
while (s[j] && isdigit(s[j])) {
sum = sum * 10 + (s[j] - '0');
j++;
}
char t = s[j];
if (sum) for (int k = 0; k < sum; k++) printf("%c", t);
else printf("%c", t);
i = j + 1; //跳过输出的字符
}
}
return 0;
}
这题还是大整数的模板,就是加了两个逆转数字为回文数和判断回文数的函数。要注意的是:必须判断原始的数字是否是回文数,不然就会有三个测试点答案错误。
#include
#include
#include
using namespace std;
const int N = 1010;
struct bign {
int d[N], len;
bign() {
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char s[]) {
bign a;
a.len = strlen(s);
for (int i = 0; s[i]; i++) {
a.d[i] = s[a.len - i - 1] - '0';
}
while (a.len > 1 && a.d[a.len - 1] == 0) a.len--;
return a;
}
bign add(bign a, bign b) {
bign t;
int carry = 0;
for (int i = 0; i < a.len || i < b.len; i++) {
int r = (carry + a.d[i] + b.d[i]);
t.d[t.len++] = r % 10;
carry = r / 10;
}
if (carry) t.d[t.len++] = carry;
return t;
}
bool isPalin(bign a) {
for (int left = 0, right = a.len - 1; left < right; left++, right--) {
if (a.d[left] != a.d[right]) return false;
}
return true;
}
bign bePalin(bign a) {
bign t = a; //逆转过程中, 不用处理后面的0变成前导0的情况
reverse(t.d, t.d + t.len);
return t;
}
bign print(bign a) {
for (int i = a.len - 1; i >= 0; i--) {
printf("%d", a.d[i]);
}
}
int main() {
char s[N];
scanf("%s", s);
bign a = change(s), b, c;
if (isPalin(a)) { //原始的数字是回文数, 就不需要经过这个逆转-相加-再逆转-相加…的过程
print(a); printf(" is a palindromic number.\n");
} else {
int flag = 1;
for (int i = 0; i < 10; i++) {
b = bePalin(a); //b是a的逆转数
c = add(a, b); //c是它们的和
print(a); printf(" + "); print(b); //打印过程
printf(" = "); print(c); printf("\n");
if (isPalin(c)) { //c在10步以内变成回文数
print(c); printf(" is a palindromic number.\n");
flag = 0; break;
} else a = c; //把和c赋给原始的数字, 重复操作直到C在10步以内变成回文数
} //原始的数字在10步以内没有变成回文数
if (flag) printf("Not found in 10 iterations.\n");
}
return 0;
}
注释写的很清楚了。这里使用了c_str()函数用printf输出字符串。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct student {
string name;
int gp, gm, gf, g; //在线编程成绩 期中考试成绩 期末考试成绩 总评
};
bool cmp(student a, student b) { //输出顺序为按照总评分数递减; 若有并列,则按学号递增
return a.g != b.g ? a.g > b.g : a.name < b.name; //string可以直接比较大小
}
map<string, int> idx; //记录学生姓名和序号的映射
int main() {
int p, n, m, score, cnt = 1;
scanf("%d%d%d", &p, &n, &m);
vector<student> v, ans; //合为一张的成绩单 获得合格证书的学生名单
string s;
for (int i = 0; i < p; i++) {
cin >> s >> score;
if (score >= 200) { //有的成绩不存在则表示为-1
v.push_back(student{s, score, -1, -1, 0});
idx[s] = cnt++;
}
}
for (int i = 0; i < m; i++) {
cin >> s >> score; //所有人必须要编程>=200分
if (idx[s] != 0) v[idx[s] - 1].gm = score; //不然不记录后面的成绩
}
for (int i = 0; i < n; i++) {
cin >> s >> score;
if (idx[s] != 0) {
int temp = idx[s] - 1;
v[temp].gf = v[temp].g = score; //总评分数(四舍五入精确到整数)
if (v[temp].gm > v[temp].gf) v[temp].g = int(round(v[temp].gm * 0.4 + v[temp].gf * 0.6));
}
}
for (int i = 0; i < v.size(); i++) //总评获得不少于60分
if (v[i].g >= 60) ans.push_back(v[i]);
sort(ans.begin(), ans.end(), cmp);
for (int i = 0; i < ans.size(); i++) //至少存在1个合格的学生
printf("%s %d %d %d %d\n", ans[i].name.c_str(), ans[i].gp, ans[i].gm, ans[i].gf, ans[i].g);
return 0;
}
部分正确 13
,错了一个测试点,不知道错在哪里?
不过这里使用了一下
头文件,尽管这些函数自己也不是不能写,但是有现成的还是更加方便。这个头文件里面的函数都很好记忆,因为所有函数都遵循这一原型:字符测试函数:int isxxxx(int); 字符转换函数:int toxxxx(int)
。接受整型参数,返回整型数据。常用的函数包括isalpha、isdigit、isalnum、isgraph、islower、isupper、tolower、toupper
。
#include
#include
#include
char password[100];
void judge(char s[]) {
if (strlen(s) < 6) {
printf("Your password is tai duan le.\n");
} else {
int sf = 0, zf = 0; //数字标识、字母标识
for (int i = 0; s[i]; i++) {
if (!isalnum(s[i]) && s[i] != '.') { //有不合法字符
printf("Your password is tai luan le.\n");
return;
}
if (isalpha(s[i])) zf = 1;
if (isdigit(s[i])) sf = 1;
}
if (zf && sf) printf("Your password is wan mei.\n"); //既有字母也有数字
else if (zf && !sf) printf("Your password needs shu zi.\n"); //没有数字
else if (!zf && sf) printf("Your password needs zi mu.\n"); //没有字母
}
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
scanf("%s", password);
judge(password);
}
return 0;
}
修正了。题目的要求是每行给出一个用户设置的密码,为不超过80个字符的非空字符串,以回车结束
,但是字符串里面可能会有空格(从测试数据看,一定有的),所以不能直接用scanf("%s") / cin
。因此要用类似gets的手段输入一行。
然而gets废止了,可以使用fgets(小心前面输入整数留下来的换行符!)或者使用cin.getline(char数组, 数组大小) / getline(cin, string类型)
。同样的,要用getline接收一行字符,也要getchar读取一下换行符,否则只有换行符会被读进getline中。
#include
#include
#include
void judge(char s[]) {
if (strlen(s) < 6) {
printf("Your password is tai duan le.\n");
} else {
int sf = 0, zf = 0; //数字标识、字母标识
for (int i = 0; s[i]; i++) {
if (isalpha(s[i])) zf = 1;
else if (isdigit(s[i])) sf = 1;
else if (s[i] != '.') { //有不合法字符
printf("Your password is tai luan le.\n"); return;
}
}
if (zf && sf) printf("Your password is wan mei.\n"); //既有字母也有数字
else if (zf && !sf) printf("Your password needs shu zi.\n"); //没有数字
else if (!zf && sf) printf("Your password needs zi mu.\n"); //没有字母
}
}
char password[100];
int main() {
int n;
scanf("%d", &n);
getchar(); //小心前面输入整数留下来的换行符!
while (n--) {
fgets(password, 100, stdin);
int len = strlen(password);
if (password[len - 1] == '\n') password[len - 1] = '\0';
judge(password);
}
return 0;
}
冠军到靶心的距离最短。
#include
#include
struct cord {
int id, x, y;
double distance;
} dis[10010];
double dist(int x, int y) { return sqrt(1.0 * (x * x + y * y)); }
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &dis[i].id, &dis[i].x, &dis[i].y);
dis[i].distance = dist(dis[i].x, dis[i].y);
}
int min = 0, max = 0; //冠军和菜鸟的序号
for (int i = 0; i < n; i++) {
if (dis[i].distance > dis[max].distance) max = i;
if (dis[i].distance < dis[min].distance) min = i;
}
printf("%04d %04d\n", dis[min].id, dis[max].id);
return 0;
}
2 <= N <= 10000,差值最大不会超过10000。
#include
int hash[20010] = {0};
int main() {
int n;
scanf("%d", &n);
int t, diff;
for (int i = 1; i <= n; i++) {
scanf("%d", &t);
diff = i >= t ? i - t : t - i;
hash[diff]++;
}
for (int i = 10000; i >= 0; i--) {
if (hash[i] > 1) printf("%d %d\n", i, hash[i]);
}
return 0;
}
外观数列是指具有以下特点的整数序列:
d, d1, d111, d113, d11231, d112213111, ...
它从不等于 1 的数字 d 开始,序列的第 n+1 项是对第 n 项的描述。比如第 2 项表示第 1 项有 1 个 d,所以就是 d1;第 2 项是 1 个 d(对应 d1)和 1 个 1(对应 11),所以第 3 项就是 d111。又比如第 4 项是 d113,其描述就是 1 个 d,2 个 1,1 个 3,所以下一项就是 d11231。当然这个定义对 d = 1 也成立。本题要求你推算任意给定数字d的外观数列的第N项。
1 8
1123123111
和B1078 字符串压缩与解压 (20 分)
差不多,能够在一个字符串中把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示,这个题就很简单了。大概自己动手手算模拟一下就行了。顺便说一句,题目中给出的不是d=1的情况,因此手动计算的时候可能会有点疑惑。
不过答案字符数组要开大一点,不然最后一个测试点会段错误。
#include
#include
const int maxn = 200000;
char ans[maxn], changed[maxn];
int len = 0;
int main() {
int n, d;
scanf("%d%d", &d, &n);
if (n == 1) printf("%d", d);
else if (n == 2) printf("%d1", d);
else {
changed[0] = d + '0'; changed[1] = '1'; changed[2] = '\0';
for (int i = 3; i <= n; i++) { //从第三项开始
for (int j = 0; changed[j]; ) {
char t = changed[j]; int k = 0;
while (changed[j + k] && changed[j + k] == t) k++;
ans[len++] = changed[j];
ans[len++] = k + '0'; //k代表一个字符重复的次数
j += k;
}
ans[len] = '\0';
if (i < n) {
strcpy(changed, ans); len = 0;
}
}
printf("%s", ans);
}
return 0;
}
每次 PAT 考试结束后,考试中心都会发布一个考生单位排行榜。本题就请你实现这个功能。
准考证号 得分 学校
其中准考证号是由 6 个字符组成的字符串,其首字母表示考试的级别:B代表乙级,A代表甲级,T代表顶级;得分是 [0, 100] 区间内的整数;学校是由不超过 6 个英文字母组成的单位码(大小写无关)。注意:题目保证每个考生的准考证号是不同的。排名 学校 加权总分 考生人数
其中排名是该单位的排名(从 1 开始);学校是全部按小写字母输出的单位码;加权总分定义为乙级总分/1.5 + 甲级总分 + 顶级总分*1.5的整数部分;考生人数是该属于单位的考生的总人数。10
A57908 85 Au
B57908 54 LanX
A37487 60 au
T28374 67 CMU
T32486 24 hypu
A66734 92 cmu
B76378 71 AU
A47780 45 lanx
A72809 100 pku
A03274 45 hypu
5
1 cmu 192 2
1 au 192 3
3 pku 100 1
4 hypu 81 2
4 lanx 81 2
两个unordered_map,因为这个题需要映射也需要排序,但map的排序不是我们需要的排序,map也不允许使用sort函数,只好把它们作为统计数据的中间结构,而且为了加快速度,我们要使用unordered_map,不然会有测试点通不过。其中,一个cnt用来存储某学校名称对应的参赛人数,另一个sum计算某学校名称对应的总加权成绩。每次学校名称string school都要转化为全小写。
然后,将两个map中所有学校的信息都保存在vector ans中,类型为node,node中包括学校姓名、加权总分(此时对最后的总和取整数部分)、参赛人数。之后使用自定义的cmp函数对ans数组排序。
对于排名(同样的成绩有同样的排名,但是每个排名都会占据一个排位)的处理:设立rank表示排名,初始化为1,如果i > 0并且i指向的成绩大于前面的成绩,则rank等于数组下标+1(即等于排位),否则rank不变。
在这道题中使用了B1095的一些经验。如使用unordered_map;for (auto it : cnt) ans.push_back(node{it.first, (int)sum[it.first], cnt[it.first]});
;排序时传递引用参数;复杂情况下用vector替代数组……
另外,map和vector都可以用operator[]进行访问,map是用[]中的数据作为key进行查询,而vector是用[]中的数作为下标进行访问。如果在用operator[]进行访问的时候出现了越界情况(即map没有这个键值对,或vector的大小小于下标数值),比如下面使用两个map,在没有这个key的情况下就进行访问并改变了value
,会发生什么状况?(原文链接:https://blog.csdn.net/qq_34228570/article/details/79905443)
struct node{ int a{5}; };
int main() {
map<string, node> m1;
cout<< m1["s"].a <<endl;
map<string, int> m2;
cout<< m2["s"] <<endl;
vector<node> v1(3); //需要指定vector大小,否则不能在没有push_back的情况用下标访问
cout<< v1[0].a <<endl;
vector<int> v2(3);
cout<< v2[0];
}
结果:
5
0
5
0
由上面示例程序可看出map和vector(需要指定vector大小)在越界情况都会给出此类型的默认值,如果是基本类型,则返回零值;如果是struct或class,如果里面的变量定义了默认值,则返回定义的默认值,如果没有,返回零值。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
struct node {
string school;
int tws, ns; //加权总分 考生人数
};
bool cmp(const node &a, const node &b) {
if (a.tws != b.tws) return a.tws > b.tws; //首先按加权总分排行
else if (a.ns != b.ns) return a.ns < b.ns; //按考生人数升序输出
else return a.school < b.school; //按单位码的字典序输出
}
int main() {
int n; scanf("%d", &n); //map本身不允许外部排序
unordered_map<string, int> cnt; //存储某学校名称对应的参赛人数
unordered_map<string, double> sum; //存储某学校名称对应的总分(非整数部分)
//只有一个学校得到了全部分数, 再取整数部分
for (int i = 0; i < n; i++) {
string id, school;
cin >> id;
double score; scanf("%lf", &score);
cin >> school; //学校名称string要转化为全小写
for (int j = 0; j < school.length(); j++)
school[j] = tolower(school[j]);
if (id[0] == 'B') score /= 1.5;
else if (id[0] == 'T') score *= 1.5;
sum[school] += score; //有默认初值
cnt[school]++;
}
vector<node> ans;
for (auto it : cnt) ans.push_back(node{it.first, (int)sum[it.first], cnt[it.first]});
sort(ans.begin(), ans.end(), cmp);
printf("%d\n", (int)ans.size());
int rank = 1;
for (int i = 0; i < ans.size(); i++) {
if (i > 0 && ans[i].tws != ans[i - 1].tws) rank = i + 1;
printf("%d ", rank);
cout << ans[i].school;
printf(" %d %d\n", ans[i].tws, ans[i].ns);
}
return 0;
}
本题就要求你,对任何一对给定的正整数,倒着输出它们的乘积。
5 7
53
这一题没讲清楚,但是从测试数据来看,要排除倒排后的前导0。如203 100
,应该要输出302
,中间的0不用去掉。
#include
int main() {
int a, b, c;
scanf("%d%d", &a, &b);
c = a * b;
int nums[10], size = 0, flag = 1;
do {
while (c % 10 == 0 && flag) c /= 10; //排除倒排后的前导0
flag = 0;
nums[size++] = c % 10;
c /= 10;
} while (c > 0);
for (int i = 0; i < size; i++) {
printf("%d", nums[i]);
}
printf("\n");
return 0;
}
不同的值,简单整数映射。
#include
int hashtable[20000] = {0};
int main() {
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) <