#include
using namespace std;
char op[1000];
char ed[10][5]={"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
int boss[100];
int pep=0,j=0;
int main()
{
cin>>op; //char数组可以直接输入字符串
for(int i=0;op[i]!='\0';i++)
{
pep+=op[i]-'0';
}
while(pep!=0)
{
boss[j]=pep%10;
pep/=10;
j++;
}
j--;
for(int i=j;i>=0;i--)
{
cout<<ed[boss[i]];
if(i!=0) cout<<' ';
}
return 0;
}
#include
#include
using namespace std;
int main()
{
int cnt;
scanf("%d",&cnt);
while(cnt--)
{
char str[1000];
cin>>str;
int i,state=0,Afront=0,Amiddle=0,Alater=0;
for(i=0;i<strlen(str);i++)
{
if(str[i]!='P'&&str[i]!='A'&&str[i]!='T')
break;
if(str[i]=='P')
if(state==0)
state=1;
else
break;
if(str[i]=='A')
if(state==1)
state=2;
else if(state==2)
Amiddle++;
else if(state==0)
Afront++;
else
Alater++;
if(str[i]=='T')
if(state==2)
state=3;
else
break;
}
while(Amiddle--)
{
Alater-=Afront;
}
if(state!=3||Afront!=Alater||i<strlen(str))
cout<<"NO"<<endl;
else
cout<<"YES"<<endl;
}
return 0;
}
分析题目三个通过条件可知:字符串中不能出现P、A、T之外的字符;PT之间只有一个A时(PAT),前后可以加上相同个数的A;PT中每多一个A(>2),后面加上前面个数个A,
统计字符串中P、T前中后A的个数判断即可
#include
using namespace std;
typedef struct Student
{
char name[100];
char number[100];
int score;
}student;
student _max={"","",0},_min={"","",100};
int main()
{
int cnt;
cin>>cnt;
while(cnt--)
{
student use;
cin>>use.name;
cin>>use.number;
cin>>use.score;
if(use.score>_max.score)
_max=use;
if(use.score<_min.score)
_min=use;
}
cout<<_max.name<<" "<<_max.number<<endl;
cout<<_min.name<<" "<<_min.number;
return 0;
}
注意 typedef 的用法,还有 max min new 不可以作为变量名使用,再提醒一下:结构体之间是可以赋值的
#include
using namespace std;
int process[4500]; //过程数
int cnt[101]; //当前数
void progress(int x)
{
if(process[x]) //避免重复记录
return;
while(x!=1)
{
if(x%2==0)
x/=2;
else
x=(x*3+1)/2;
process[x]=1;
}
}
int main()
{
int num,tmp;
cin>>num;
while(num--)
{
cin>>tmp;
cnt[tmp]=1;
progress(tmp);
}
int firstblood=0; //题目要求最后一个数字后没有空格
for(int i=100;i>0;i--)
{
if(cnt[i]&&process[i]==0&&firstblood==0)
{
firstblood=1;
cout<<i;
}
else if(cnt[i]&&process[i]==0&&firstblood==1)
cout<<" "<<i;
}
return 0;
}
题目意思是第一个直接执行3n+1猜想,并记录过程中的每个数,注意是过程数;后面的每个数先判断是否已经记录过,记录过就直接跳过(记录过后面算就没意义了),没记录过就执行3n+1猜想并记录过程数
#include
using namespace std;
int array[105];
int main()
{
int num,run;
cin>>num>>run;
for(int i=1;i<=num;i++)
cin>>array[i];
int begin=num-run%num+1; //解决数循环移次数的方法
for(int i=1;i<=num;i++)
{
if(begin>num) //再打印 begin 之前的
begin=1;
cout<<array[begin++]; //技巧处,先从 begin 开打印
if(i<num)
cout<<' ';
}
return 0;
}
题目本身不难,但要关注本代码的小技巧
#include
using namespace std;
char a[81][81],c;
int length=80,i=0;
int main()
{
while((c=getchar())!='\n') //典型的字符串输入
{
if(c==' ')
{
a[length][i]='\0';
length--; //从后往前
i=0;
continue;
}
else
{
a[length][i]=c;
i++;
}
}
a[length][i]='\0';
for(int j=length;j<=80;j++)
{
if(j==80)
cout<<a[j];
else
cout<<a[j]<<' ';
}
return 0;
}
特别注意:输出字符串时,程序读取到 '\0'
后面都不会输出,如 "123\0456"
,只打印 123
#include
using namespace std;
int main()
{
int a,b,flag=0;
while(cin>>a>>b) //典型的数字输入
{
if(a*b)
{
if(flag)
cout<<' ';
else
flag=1;
cout<<a*b<<' '<<b-1;
}
}
if(!flag) //题目原话 : 注意 “零多项式” 的指数和系数都是 0,但是表示为 0 0
cout<<0<<' '<<0;
return 0;
}
可以利用规律来相乘和减 1,比如例题中 3 * 4 = 12 ,- 5 * 2 = -10 ,6 * 1 = 6 ,- 2 * 0 = 0,这是对应的系数,4 - 1 = 3 ,2 - 1 = 1 ,1 - 1 = 0,这是指数,最后一个是常数 -2,指数不做运算,这是要注意的
另外本代码的 flag 有两个用处,一是解决题目要求的:结尾不能有多余空格,二是满足题目:“零多项式” 的指数和系数都是 0,但是表示为 0 0
#include
#include //此头文件也别忘了
using namespace std;
int main()
{
int cnt,A=0,B=-1,C=0,E=0,B2=0,B3=0;
double D=0,D2=0; //记得浮点数要用 double
cin>>cnt;
while(cnt--)
{
int num;
cin>>num;
if(num%5==0&&num%2==0)
A+=num;
if(num%5==1)
{
B*=-1;
B2+=num*B;
B3=1;
}
if(num%5==2)
C++;
if(num%5==3)
{
D+=num;
D2++;
}
if(num%5==4)
{
if(num>E)
E=num;
}
}
if(A==0)
cout<<"N ";
else
cout<<A<<' ';
if(B3==0)
cout<<"N ";
else
cout<<B2<<' ';
if(C==0)
cout<<"N ";
else
cout<<C<<' ';
if(D2==0)
cout<<"N ";
else
cout<<setprecision(1)<<fixed<<D/D2<<' ';
if(E==0)
cout<<"N";
else
cout<<E;
return 0;
}
本代码是每输入一个数就判断一个数,会比较方便和省时
#include
#include
#include
#include
using namespace std;
struct student
{
char id[10];
int de, cai, sum;
int flag;
}stu[1000010]; //根据题目要求元素小于等于 10的 5次方
bool cmp(student a,student b) //关键代码,用于分类排序
{
if (a.flag != b.flag) return a.flag < b.flag; //按类分
else if (a.sum != b.sum) return a.sum>b.sum; //按总分
else if (a.de != b.de) return a.de > b.de; //按德分
else return strcmp(a.id, b.id) < 0; //按准考证号分
}
int main()
{
int n, l, h;
scanf("%d%d%d", &n, &l, &h);
int m = n; //m表示及格人数
for (int i = 0; i < n; i++)
{
scanf("%s %d %d", &stu[i].id, &stu[i].de, &stu[i].cai);
stu[i].sum = stu[i].de + stu[i].cai; //先把总分算出来有利于后期处理
if (stu[i].de < l || stu[i].cai < l) //先找出不及格者
{
stu[i].flag = 5;
m--;
}
else if (stu[i].de >= h&&stu[i].cai >= h)
{
stu[i].flag = 1;
}
else if (stu[i].de >= h&&stu[i]. cai < h)
{
stu[i].flag = 2;
}
else if (stu[i].de >= stu[i].cai)
{
stu[i].flag = 3;
}
else
{
stu[i].flag = 4;
}
}
sort(stu, stu + n, cmp);
printf("%d\n", m);
for (int i = 0; i < m; i++)
{
printf("%s %d %d\n", stu[i].id, stu[i].de, stu[i].cai); //这里用 printf,不然换做 cout会超时
}
return 0;
}
本代码总体是:先分类,再排序
题目要求:考生按输入中说明的规则从高到低排序,当某类考生中有多人总分相同时,按其德分降序排列,若德分也并列,则按准考证号的升序输出
补充一点:strcmp 的用处如下
基本形式为 strcmp(str1,str2)
若str1=str2,则返回零
若str1<str2,则返回负数
若str1>str2,则返回正数
(即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止)
故代码中的 strcmp(a.id, b.id) < 0
代表升序(从小到大),记住这种表达方式~
#include
using namespace std;
int main()
{
string s;
int a, t = 0, temp = 0; //t是商 temp是余数
cin >> s >> a;
int len = s.length(); //记住长度用这种写法
t = (s[0] - '0') / a; //字符转换成数字,再求商
if ((t != 0 && len > 1) || len == 1) //之所以额外加 len,是因为后面的 for循环是从 i=1开始
cout << t;
temp = (s[0] - '0') % a;
for (int i = 1; i < len; i++) //从 i=1开始
{
t = (temp * 10 + s[i] - '0') / a;
cout << t;
temp = (temp * 10 + s[i] - '0') % a;
}
cout << " " << temp;
return 0;
}
代码内容是:模拟手动除法的过程,每次用第一位去除以B,如果得到的商不是0就输出,否则就 * 10 + 下一位,直到最后的数为余数
注意:存在除数为 0 的情况
#include
using namespace std;
int main()
{
int book[10]={0};
for(int i=0;i<10;i++)
cin>>book[i];
int index=1; //从 1开始,因为 0不能做开头
while(book[index]==0) //没有 1再往后找开头
index++;
book[index]--;
cout<<index;
index=0;
while(index<=9)
{
if(!book[index])
{
index++;
continue;
}
cout<<index;
book[index]--;
}
return 0;
}
也是用到了小技巧:先确定开头,再往后从小到大添加数字
#include
#include
using namespace std;
int main()
{
char str[10010];
cin>>str;
int len = strlen(str);
if(str[0]=='-') cout<<'-';
int pos=0; //E的下标
while(str[pos]!='E') pos++;
int exp=0; //指数
for(int i=pos+2;i<len;i++) //这里是 <号是因为 len代表长度而不是下标
exp=exp*10+(str[i]-'0');
if(exp==0) //指数为 0的特例
for(int i=1;i<=pos;i++)
{
cout<<str[i];
return 0; //一定要记得
}
if(str[pos+1]=='-') //指数为负数:考虑 0的位置
{
cout<<"0.";
for(int i=1;i<exp;i++) cout<<'0';
cout<<str[1]; //省去了不必要的麻烦
for(int i=3;i<pos;i++) cout<<str[i];
}
else //指数为正数:考虑 .的位置
{
for(int i=1;i<pos;i++)
{
if(str[i]=='.') continue; //直接略过原来的 .号
cout<<str[i];
if(exp<pos-3&&i==exp+2) cout<<'.'; //注意条件
}
for(int i=0;i<exp-(pos-3);i++) cout<<'0'; //注意条件
}
return 0;
}
本题难点就在于条件
代码顺序是:先打出指数是0的特例(省去后面麻烦),再判断指数为正与负并写出各个结果
当指数为正数时,可以直接略过原来的 .号只考虑如何加新的 . 号,其中 (i==exp+2) && (pos-3 != exp)
可以打个草稿分析(前者+2是加上了符号和 . 号)(后者-3是减去了符号和 . 号和 E 号)
(注意题目要求:正数不用输出+号、并保证所有有效位都被保留,包括末尾的 0)
#include
#include
using namespace std;
int main()
{
int first, k, n, temp;
cin >> first >> n >> k; //首地址,节点个数,反转数
int *data = new int[100005];
int *next = new int[100005]; //这两个用了new,不然运行出现栈溢出
int list[100005]; //数组list用来按顺序存放地址
//address data next
for (int i = 0; i < n; i++) //结点地址,该结点保存的整数数据,下一结点的地址
{
cin >> temp;
cin >> data[temp] >> next[temp];
}
int sum = 0; //真正在链表中的结点个数
//建立正序地址数组list
while (first != -1) //巧妙的用法
{
list[sum++] = first;
first = next[first];
}
//反转
for (int i = 0; i < sum/k ; i++) //此处的 sum 代表元素个数而不是下标,不然要先 sum--
{
reverse(begin(list) + i*k, begin(list) + i*k + k); //见补充
}
for (int i = 0; i < sum - 1; i++)
printf("%05d %d %05d\n", list[i], data[list[i]], list[i + 1]);
printf("%05d %d -1", list[sum - 1], data[list[sum-1]]); // list没有存最后一个数,所以要打印 -1
delete[] data;
delete[] next;
return 0;
}
主要步骤:先对原列表排序,再反转
注意:应该考虑输入样例中有不在链表中的结点的情况,所以用个sum计数
补充:new和delete的用法回顾 、reverse()函数的第二个参数是数组最后一个元素的下一个地址
#include
#include
using namespace std;
int main()
{
int time1,time2;
cin>>time1>>time2;
double time=(time2-time1)/100.0-(time2-time1)/100; //整型除以浮点型得浮点型
if(time>=0.5) //整形减去浮点型也得浮点型
time=(time2-time1)/100+1;
else
time=(time2-time1)/100;
int sp=time;
printf("%02d:%02d:%02d\n",sp/3600,sp%3600/60,sp%3600%60);
return 0;
}
题目本身不难,但注意代码的技巧性,本题运用了四舍五入的简易版
#include
#include
using namespace std;
int main()
{
int n;
char c;
cin >> n >> c;
int judge = ( n + 1) / 2;
int hang = sqrt(judge); //半个的完整的沙漏层数
int res = n - 2 * hang * hang + 1; //多余沙漏数
for(int i = 1; i <= hang ; i ++)
{
int num1 = i - 1;
for(int j = 0 ; j < num1 ; j ++)
cout << ' ';
int num2 = 2 * (hang - i + 1 ) - 1;
for(int j = 0; j < num2 ; j ++)
cout << c;
cout << endl;
}
for(int i = 2 ; i <= hang ; i++)
{
int num1 = hang - i;
for(int j = 0 ; j < num1 ; j ++)
cout << ' ';
int num2 = 2 * i - 1;
for(int j = 0; j < num2 ; j++)
cout << c;
cout << endl;
}
cout << res ;
return 0;
}
分析:
明确了 整个沙漏的字符数(2k*k-1)与 半个的完整的沙漏层数(k)与 每一层沙漏个数(2k-1) 的关系即可
整个沙漏的形状为: 2k-1 , 2k-3 , … ,5 ,3, 1 , 3, 5, … ,2k-3 , 2k-1 ( k 代表半个的完整的沙漏层数)
整个沙漏的字符数为 2k*k -1 (用到了等差数列求和)
补充:对于输入的 n ,其实也只需要判断(n+1)/2是不是开方数,但是不管它是不是开方数,开方的结果都是 k
等差数列求和回顾
#include
#include
using namespace std;
int main()
{
string date,date_old="2014/09/06",date_young="1814/09/06";
string name,name_old,name_young;
int n,count=0;
cin>>n;
while(n--)
{
cin>>name>>date;
if(date>="1814/09/06"&&date<="2014/09/06")
{
count++;
if(date<date_old)
{
date_old=date;
name_old=name;
}
if(date>date_young)
{
date_young=date;
name_young=name;
}
}
}
if(count)
cout<<count<<' '<<name_old<<' '<<name_young;
else
cout<<0;
return 0;
}
注意题目:这里确保每个输入的日期都是合法的,但不一定是合理的
分析:利用 string 的特性,使字符串间可以直接比较大小,然后求最值即最年轻的和最年长的,最后注意一个坑点,可能会有0个符合的年份,则只输出0
#include
#include
#include
using namespace std;
int main()
{
char str1[90], str2[90];
cin>>str1>>str2;
int len1 = strlen(str1);
int len2 = strlen(str2);
int Bool[128] = {0}; //因为 ASCII码表里的字符总共有 128个
for (int i = 0; i < len1; i++)
{
int j = 0; //重新计数
char c1;
char c2;
for ( j ; j < len2; j++)
{
c1 = str1[i];
c2 = str2[j];
if (c1 >= 'a'&&c1 <= 'z') c1 -= 32; //保证只在此循环内改变字母大小写
if (c2 >= 'a'&&c2 <= 'z') c2 -= 32;
if (c1 == c2) break; //在str2找得到str1的字符
}
if (j == len2 && Bool[c1] == 0) //判断输出并记录
{
cout<<c1;
Bool[c1] = 1;
}
}
return 0;
}
题目和代码本身不难,但要记住这种判断输出并记录 的方法
#include
#include
using namespace std;
int n, q, a[100010];
int binarySearch(int i, long long x) //二分法 找到第一个大于或者等于a[i]*q的数
{
if (a[n - 1] <= x) return n;
int left = i + 1, right = n - 1, mid; //此处赋值与二分法无关
while (left < right)
{
mid = (left + right) / 2;
if (a[mid] <= x) left = mid + 1;
else right = mid;
}
return left; //right也可以
}
int main()
{
cin>>n>>q;
for(int i = 0; i < n; i++) cin>>a[i];
sort(a, a + n);
int ans = 1;
for (int i = 0; i < n; i++)
{
int j = binarySearch(i,(long long)a[i] * q);
ans = max(ans, j - i);
}
cout<<ans;
return 0;
}
有点阅读理解的味道,题目的意思:最大与最小相除小于p,求如何使数列中间的元素更多
分析:从最小数开始,不断增大,找出 与 最大数开始往后减小(用二分法)的最下标大差值
补充:就当回顾二分法的适用题目与方法
#include
#include
using namespace std;
int main()
{
char a[128]={0},c;
while((c=getchar())!='\n')
a[c]=1;
while((c=getchar())!='\n')
if((isupper(c) && a[43]) || a[toupper(c)]==1) continue;
else cout<<c;
return 0;
}
题目和代码本身简单,但需要掌握两个函数:isupper() 和 toupper()
isupper(c): 当参数c为大写英文字母(A-Z)时,返回 非0 值,否则返回 0
toupper(c): 能将字符c转换为大写英文字母
两者都包含于头文件
中
#include
#include
using namespace std;
long long a, b, c, d;
long long gcd(long long a, long long b)
{
return b == 0 ? a : gcd(b, a % b);
}
void func(long long m, long long n)
{
if (m * n == 0)
{
printf("%s", n == 0 ? "Inf" : "0"); //多掌握些这样的用法
return ;
}
bool flag = ((m < 0 && n > 0) || (m > 0 && n < 0));
m = abs(m); n = abs(n);
long long x = m / n;
printf("%s", flag ? "(-" : "");
if (x != 0) printf("%lld", x);
if (m % n == 0)
{
if(flag) printf(")");
return ;
}
if (x != 0) printf(" ");
m = m - x * n;
long long t = gcd(m, n);
m = m / t; n = n / t;
printf("%lld/%lld%s", m, n, flag ? ")" : "");
}
int main()
{
scanf("%lld/%lld %lld/%lld", &a, &b, &c, &d);
func(a, b); printf(" + "); func(c, d); printf(" = "); func(a * d + b * c, b * d); printf("\n");
func(a, b); printf(" - "); func(c, d); printf(" = "); func(a * d - b * c, b * d); printf("\n");
func(a, b); printf(" * "); func(c, d); printf(" = "); func(a * c, b * d); printf("\n");
func(a, b); printf(" / "); func(c, d); printf(" = "); func(a * d, b * c);
return 0;
}
分析:func(m, n)
的作用是对 m / n 的分数进行化简
在 func()函数中,先看 m 和 n 里面是否有0(m * n 是否等于0),再看 flag,flag=true
表示 m 和 n 异号,那么后面要添加”(-“
和”)”
,再将 m和 n 取绝对值,然后计算 x = m/n
,x 表示可提取的整数部分(带分数),接着根据 m % n 是否等于 0 的结果判断后面还有没有小分数,若有小分数,然后求 m 和 n 的最大公约数 t ,让 m 和 n 都除以 t 进行化简,最后输出即可
补充:判断 m 和 n 是否异号千万不要写成判断 m * n 是否小于 0,因为 m * n 的结果可能超过了 long long int 的长度,导致溢出大于 0,如果这样写的话会有一个测试点无法通过
#include
#include
using namespace std;
int main()
{
int n, a[100], b[100], i, j;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
cin >> b[i];
for (i = 0; i < n - 1 && b[i] <= b[i + 1]; i++);
for (j = i + 1; a[j] == b[j] && j < n; j++);
if (j == n)
{
cout << "Insertion Sort" << endl;
sort(a, a + i + 2); //第二个参数 + 的是数量
}
else
{
cout << "Merge Sort" << endl;
int k = 1, flag = 1;
while(flag) //直到符合
{
flag = 0;
for (i = 0; i < n; i++)
{
if (a[i] != b[i])
flag = 1;
}
k *= 2; //一次增一倍
for (i = 0; i < n / k; i++)
sort(a + i * k, a + (i + 1) * k); //此处排列的方式是一次排一列
sort(a + n / k * k, a + n); //将一列中剩下的元素也进行排列(代指余数)
}
}
for (j = 0; j < n; j++)
{
if (j != 0) printf(" "); //类似于 firstblood
printf("%d", a[j]);
}
return 0;
}
先简单回顾一下插入排序(图1)和归并排序(图2)
分析:先将 i 指向序列中满足从左到右是从小到大顺序的最后一个下标,再将 j 指向从 i + 1 开始,第一个不满足a[j] == b[j]
的下标,如果 j 顺利到达了下标 n,说明是插入排序,否则说明是归并排序
过程:如果是插入排序,再下一次的序列是sort(a, a+i+2)
(这里别忘了 sort 函数第二个参数 + 的是数量),而如果是归并排序,直接对原来的序列进行模拟归并过程
补充:插入排序是由前之后排序,归并排序是整体排序,一次排一列
#include
using namespace std;
int main()
{
int a[128]={0};
char tmp;
while((tmp=getchar())!='\n') //买
{
a[tmp]++;
}
while((tmp=getchar())!='\n') //想要
{
a[tmp]--;
}
int firstblood=1,votal1=0,votal2=0;
for(int i=0;i<128;i++)
{
if(a[i]<0)
{
if(firstblood)
firstblood=0;
votal1+=-a[i]; //可以直接写负数
}
else
{
votal2+=a[i];
}
}
if(firstblood)
{
cout<<"Yes "<<votal2;
}
else
cout<<"No "<<votal1;
return 0;
}
比较奇特的一点是:由 一个数组 和 数组的各个值的正负 判断 Yes or No
分析:想要记作 - - ,买了的记作++ ,那么最后 正数即为多余的,负数即为缺少的
#include
#include
using namespace std;
int main()
{
int alpha[26]={0};
char ch;
while((ch=getchar())!='\n')
if(isalpha(ch)) //判断是否为英文字母
alpha[tolower(ch)-'a']++; //根据题意 ,只存小写英文字母
int _max=0,sp;
for(int i=25;i>=0;i--)
if(alpha[i]>=_max)
sp=i,_max=alpha[i];
cout<<char(sp+'a')<<' '<<_max; //用 char(int)的方式输出字符
return 0;
}
题目和代码本身简单,但需要掌握两个函数:isalpha() 和 tolower()
isalpha(c): 当参数c为英文字母 (A-Z) 或 (a-z) 时,返回 非0 值,否则返回 0
isalnum(c):当字符变量c为字母或数字,返回 非0 值,否则返回 0
tolower(c): 能将字符c转换为小写英文字母
两者都包含于头文件
中
#include
#include
#include
using namespace std;
string a[13] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"}; // 0到 12的火星文
string b[13] = {"####", "tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"}; //进位以后的 12个高位数字
string s;
int len;
void tomars(int t) //转字符串
{
if (t / 13) cout << b[t / 13]; //进位数
if ((t / 13) && (t % 13)) cout << " "; //有个位数
if (t % 13 || t == 0) cout << a[t % 13]; // 0 或 个位数
}
void toearth() //转数字
{
int t1 = 0, t2 = 0;
string s1 = s.substr(0, 3), s2;
if (len > 4) s2 = s.substr(4, 3);
for (int j = 1; j < 13; j++)
{
if (s1 == a[j] || s2 == a[j]) t2 = j;
if (s1 == b[j]) t1 = j;
}
cout << t1 * 13 + t2;
}
int main()
{
int n;
cin >> n;
getchar(); //非常关键!
for (int i = 0; i < n; i++)
{
getline(cin, s);
len = s.length();
if (isdigit(s[0]))
{
int num=0;
for(int i=0;i<s.length();i++)
num=num*10+(s[i]-'0'); //注意不是 +=
tomars(num);
}
else
toearth();
cout << endl;
}
return 0;
}
分析:本代码用到了 string 数组,和各种各样的函数
substr ( 开始,长度),相当于复制,举例如下:
string s1 = s2.substr(0, 3);
atoi(c) 将字符 c 转换成数字,举例如下:
str[100]="00100" ; cout<<atoi(str)<<endl; output:100
//头文件
strcmp(str1,str2),若str1=str2
,则返回零;若str1
str1>str2
,则返回正数
strcpy(str1,str2),将str2
中的字符复制到str1
中
补充:
string s;
getline(cin,s);
cout<<s.size();
//下面实例说明了空格在 getline中也算一个字符
input:a b
output:3
#include
#include
using namespace std;
int v[100000];
using namespace std;
int main()
{
int n, _max = 0, cnt = 0;
cin>>n;
int a[100005],b[100005];
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(a,a+n);
for (int i = 0; i < n; i++)
{
if(a[i] == b[i] && b[i] > _max)
v[cnt++] = b[i];
if (b[i] > _max)
_max = b[i];
}
cout<<cnt<<endl;
for(int i = 0; i < cnt; i++)
{
if (i != 0) cout<<' ';
cout<<v[i];
}
cout<<endl;
return 0;
}
由主元的定义:比前面的数都大,比后面的数都小,而相当于主元本身已经排好序了,那么将原数组排好序,对应位置相同的有可能是主元,再加上当前最大值的验证方法,一重循环即可
然后我似乎也找到有些人热衷于在末尾加换行符的原因了:不加就可能过不了测试点
#include
#include
using namespace std;
int main()
{
string a, b, c;
cin >> a >> b;
int lena = a.length(), lenb = b.length();
reverse(a.begin(), a.end()); //反转字符串
reverse(b.begin(), b.end());
if (lena > lenb)
b.append(lena - lenb, '0');
else
a.append(lenb - lena, '0');
char str[14] = {"0123456789JQK"};
for (int i = 0; i < a.length(); i++)
{
if (i % 2 == 0)
c += str[(a[i] - '0' + b[i] - '0') % 13];
else
{
int temp = b[i] - a[i];
if (temp < 0) temp = temp + 10;
c += str[temp];
}
}
for (int i = c.length() - 1; i >= 0; i--)
cout << c[i];
return 0;
}
题目和代码本身简单,但需要注意:题目并没有说明白数字A可能比数字B长,这时需要短的数字补零(反转字符串即可)
补充:关于 string类 中 append 的用法,举例如下:
string a, b;
cin >> a >> b;
int lena = a.length(), lenb = b.length();
if (lena > lenb) //若 a 比 b 长,就补齐 b
b.append(lena - lenb, '0'); //第一个参数是个数,第二个参数是补充的字符
cout << b;
input:12345 123
output:12300
#include
#include
using namespace std;
int main()
{
int n;
cin >> n;
double sum = 0.0, temp;
for (int i = 1; i <= n; i++)
{
cin >> temp;
sum += temp * i * (n - i + 1);
}
printf("%.2f", sum);
return 0;
}
单纯就是数学题,每个数出现的个数是(左边数的个数+1)*(右边数的个数+1)
#include
#include
#include
#include
using namespace std;
int cmp(int a, int b) {return a > b;}
int main()
{
int N, m, n, t = 0; //t代表总个数-1
scanf("%d", &N);
for (n = sqrt((double)N); n >= 1; n--) //计算矩阵(sqrt只适用于浮点型)
{
if (N % n == 0)
{
m = N / n;
break;
}
}
vector<int> a(N); //动态数组
for (int i = 0; i < N; i++)
scanf("%d", &a[i]);
sort(a.begin(), a.end(), cmp);
vector<vector<int> > b(m, vector<int>(n)); //二维动态数组
int level = m / 2 + m % 2; // level层数算法
for (int i = 0; i < level; i++) //矩阵排序
{
for (int j = i; j <= n - 1 - i && t <= N - 1; j++)
b[i][j] = a[t++];
for (int j = i + 1; j <= m - 2 - i && t <= N - 1; j++)
b[j][n - 1 - i] = a[t++];
for (int j = n - i - 1; j >= i && t <= N - 1; j--)
b[m - 1 - i][j] = a[t++];
for (int j = m - 2 - i; j >= i + 1 && t <= N - 1; j--)
b[j][i] = a[t++];
}
for (int i = 0; i < m; i++)
{
for (int j = 0 ; j < n; j++)
{
printf("%d", b[i][j]);
if (j != n - 1) printf(" ");
}
printf("\n");
}
return 0;
}
解析:首先计算行数m和列数n的值,题目要求(m >= n),n 等于根号N的整数部分一直往 1 减,直到 N % n == 0,m 等于 N / n,再算 层数level(尽量多),等于 m / 2 + m % 2(所以用 m),最后矩阵排序,按顺时针螺旋方向,数字从大到小,输入数组元素,最后正常输出数组即可
补充:输入数组元素时,按照 4 个 for 循环输入,注意第二个和第四个 for 循环的起始位置和结束位置(输入元素总量少一个)
当不想占用栈太多内存时,可以用 vector 充当动态数组,因为其数据存储在堆中,用法如下:
//建立一维动态数组
vector<int> a(10);
//建立二维动态数组
vector<vector<int> > b(10, vector<int>(10));
//输入
cin>>a[0]>>b[0][0];
//输出
cout<<a[0]<<' '<<b[0][0];
input:5 5
output:5 5
#include
#include
int main() {
double a,b,c,d;
scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
double a1,a2;
a1=a*c*cos(b+d);//三角复合函数
a2=a*c*sin(b+d);//三角复合函数
if (fabs(a1)<=0.01)
a1=0.00;
if (fabs(a2)<=0.01)
a2=0.00;
if (a2<0)
printf("%.2lf-%.2lfi",a1,-a2);
else
printf("%.2lf+%.2lfi",a1,a2);
return 0;
}
题目可能有问题,没有说清楚要不要四舍五入,顺便补充一个关于四舍五入小知识点
double a=0.004;
double b=0.005;
printf("%.2lf\n",a);
printf("%.2lf",b);
//从结果来看,对于格式化,程序会自动四舍五入
output:0.00
0.01
#include
#include
using namespace std;
int main()
{
vector<vector<string> > v; //二重动态数组
for(int i = 0; i < 3; i++)
{
string s;
getline(cin, s);
vector<string> row;
int j = 0, k = 0;
while(j < s.length())
{
if(s[j] == '[')
{
while(k++ < s.length())
{
if(s[k] == ']')
{
row.push_back(s.substr(j+1, k-j-1)); //(长度,开始)
break;
}
}
}
j++;
}
v.push_back(row);
}
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int a, b, c, d, e;
cin >> a >> b >> c >> d >> e;
if(a > v[0].size() || b > v[1].size() || c > v[2].size() || d > v[1].size() || e > v[0].size() || a < 1 || b < 1 || c < 1 || d < 1 || e < 1)
{
cout << "Are you kidding me? @\\/@" << endl;
continue;
}
cout << v[0][a-1] << "(" << v[1][b-1] << v[2][c-1] << v[1][d-1] << ")" << v[0][e-1] << endl; //注意减一
}
return 0;
}
问题和代码本身不难,但用到了二重动态规划(string类型),举例如下:
vector<vector<string> > v; //注意对于 string 类型不用设置长度
vector<string> row;
string a;
cin>>a;
row.push_back(a);
v.push_back(row);
cout<<v[0][0];
input:12345
output:12345
#include
#include
#include
using namespace std;
int main()
{
int n, cnt = 0;
char a[50], b[50];
double temp, sum = 0.0;
cin >> n;
for(int i = 0; i < n; i++)
{
scanf("%s", a);
sscanf(a, "%lf", &temp); //将 a 输入到 temp 中
sprintf(b, "%.2f",temp); //将 temp 输入到 b 中
int flag = 0;
for(int j = 0; j < strlen(a); j++) //以 a 为参照物进行筛选排除,很关键的一步操作
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 %.2f", sum);
else if(cnt > 1)
printf("The average of %d numbers is %.2f", cnt, sum / cnt);
else
printf("The average of 0 numbers is Undefined");
return 0;
}
先讲解 sscanf 和 sprintf 的作用:
sscanf(a,"%lf",&temp)
:从 a(字符串)中读进与指定格式相符的数据输入到 temp 中
sprintf(b,"%.2f",temp)
:将 temp 格式化输入到 b(字符串)中 (注意没有&号)
下面以部分本代码举例:
char a[50], b[50];
double temp;
scanf("%s", a);
sscanf(a, "%lf", &temp);
sprintf(b, "%.2f",temp);
cout<<a<<endl<<temp<<' '<<b;
//从下列实例看出排除了多种不合法数据
input:1.23.4
ouput:1.23 1.23
input:45.678
ouput:45.678 45.68
input:aaa
ouput:4.94066e-324 0.00
以其他例子举例:
char str[100]="1234:3.14,hello",arr[10];
int n;
double db;
sscanf(str,"%d:%lf,%s",&n,&db,arr); //注意这里不能用 %.2lf(似乎是规定)
cout<<n<<' '<<db<<' '<<arr<<endl;
char str2[100],arr2[]="hello";
int n2=1234;
double db2=3.14;
sprintf(str2,"%d:%.2lf,%s",n2,db2,arr2);
cout<<str2;
//下面实例说明此函数可以转换多个参数
output:
1234 3.14 hello
1234:3.14,hello
#include
#include
#include
using namespace std;
struct node
{
string name;
int height;
};
int cmp(struct node a, struct node b)
{
return a.height != b.height ? a.height > b.height : a.name < b.name; //掌握这种表达方法
}
int main()
{
int n, k, m; //人数 排数 某一行
cin >> n >> k;
vector<node> stu(n); //直接用 vector 容器代替了数组
for(int i = 0; i < n; i++)
{
cin >> stu[i].name >> stu[i].height;
}
sort(stu.begin(), stu.end(), cmp);
int t = 0, row = k; // t代表排序进行的位置 row代表当前行数
while(row--)
{
if(row == k-1) //最后一排
m = n - n / k * (k - 1);
else
m = n / k;
vector<string> ans(m);
ans[m / 2] = stu[t].name;
// 左边一列
int j = m / 2 - 1;
for(int i = t + 1; i < t + m; i = i + 2)
ans[j--] = stu[i].name;
// 右边一列
j = m / 2 + 1;
for(int i = t + 2; i < t + m; i = i + 2)
ans[j++] = stu[i].name;
// 输出当前排
cout << ans[0];
for(int i = 1; i < m; i++)
cout << " " << ans[i];
cout << endl;
t = t + m;
}
return 0;
}
分析:因为是面对拍照者,后排的人输出在上方,前排输出在下方,每排人数为 N / K,多出来的人全部站在最后一排,最中间一个学生应该排在 m / 2 的下标位置(题目的要求有歧义),即ans[m / 2] = stu[t].name
,然后排左边一列,ans 数组的下标 j 从 m / 2 - 1 开始,一直往左 j - -,而对于 stu 的下标 i 从 t + 1 开始,每次隔一个人选取,排右边的队伍同理,最后输出当前已经排好的 ans 数组
#include
#include
using namespace std;
int main()
{
int N, sum = 0, temp;
scanf("%d", &N);
for (int i = 0; i < N; i++)
{
scanf("%d", &temp);
sum += temp * 10 * (N - 1) + temp * (N - 1);
}
printf("%d", sum);
return 0;
}
技巧性题目,在 sum 累加的过程中,对于每一个输入的数字 temp,能够放在个位也能够放在十位,所以每个数字 temp 都能在个位出现 (N-1) 次,十位出现 (N-1) 次,在个位产生的累加效果为 temp * (N-1) ,而在十位产生的累加效果为 temp * (N-1) * 10,所以 sum 即是答案
#include
#include
#include
using namespace std;
int main()
{
int n, m, temp, k; //学生人数 多选题的个数 选项个数 正确选项个数
scanf("%d%d", &n, &m);
vector<set<char> > right(m); //题目选项
vector<int> total(m), wrongCnt(m); //每一题的总分 某题错误的人数
for(int i = 0; i < m; i++)
{
scanf("%d%d%d", &total[i], &temp, &k);
for(int j = 0; j < k; j++)
{
char c;
scanf(" %c", &c);
right[i].insert(c);
}
}
for(int i = 0; i < n; i++)
{
int score = 0;
scanf("\n"); //表示忽略回车键
for(int j = 0; j < m; j++)
{
if(j != 0) scanf(" ");
scanf("(%d", &k);
set<char> st;
char c;
for(int l = 0; l < k; l++)
{
scanf(" %c", &c);
st.insert(c);
}
scanf(")");
if(st == right[j]) //st集合和该题目选项一致
score += total[j];
else
wrongCnt[j]++;
}
printf("%d\n", score);
}
int maxWrongCnt = 0;
for(int i = 0; i < m; i++)
maxWrongCnt=max(maxWrongCnt,wrongCnt[i]);
if(maxWrongCnt == 0)
printf("Too simple");
else
{
printf("%d", maxWrongCnt);
for(int i = 0; i < m; i++)
if(wrongCnt[i] == maxWrongCnt) //输入编号
printf(" %d", i + 1);
}
return 0;
}
分析:本题只要思路明确就可以很容易写出代码,要求输出 每个人的分数 和 错误人数最多的题和编号,那么肯定要设置 wrongCnt[i]
作为每题错误人数,maxWrongCnt
作为错误最多的人数,再根据题目要求设置相应的变量进行推导即可
解析:set 是一种特别好的容器,它能存储元素各不同元素作为集合,举例如下:
set<int> st,sp;
//判断两种集合包含元素是否相同 (直接用等号判断)
st==sp;
//插入 (括号内是元素值)
st.insert(5)
//查找 (括号内是元素值;若找不到则返回 st.end())
st.find(5)!=st.end()
//是否存在 (括号内是元素值;若找到返回非 0值;若找不到则返回 0)
st.count(5)
小知识:vector 封装数组,set 封装集合,queue 封装队列,list 封装链表,map 封装关联数
补充:关于 scanf()
另类的用法,举例如下:
int d;
scanf("("); //表示忽略左括号
scanf("%d",&d);
scanf(")"); //表示忽略右括号
cout<<d;
//下面实例论证上述说明是正确的
input: (45)
output: 45
input: 45
output: 45
input: )45(
output: 0
#include
#include
#include
using namespace std;
int ran[10000];
bool isprime(int a)
{
if(a <= 1) return false;
for(int i = 2; i <= sqrt((double)a); i++)
if(a % i == 0)
return false;
return true;
}
int main()
{
int n, k;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int id;
scanf("%d", &id);
ran[id] = i + 1;
}
scanf("%d", &k);
set<int> ss;
for(int i = 0; i < k; i++)
{
int id;
scanf("%d", &id);
printf("%04d: ", id);
if(ran[id] == 0)
{
printf("Are you kidding?\n");
continue;
}
if(ss.find(id) == ss.end())
ss.insert(id);
else
{
printf("Checked\n");
continue;
}
if(ran[id] == 1)
printf("Mystery Award\n");
else if(isprime(ran[id]))
printf("Minion\n");
else
printf("Chocolate\n");
}
return 0;
}
题目和代码本身不难,就是想提醒一下以后遇到素数不要老想着用欧拉线性筛法,记一下普通模板
bool isprime(int a)
{
if(a <= 1) return false;
for(int i = 2; i <= sqrt((double)a); i++)
if(a % i == 0)
return false;
return true;
}
#include
#include
using namespace std;
int a[100005];
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]); //注意要从 1 开始,因为天数不能为 0
sort(a+1, a+n+1, cmp);
int ans = 0, p = 1;
while(ans < n && a[p] > p)
{
ans++;
p++;
}
printf("%d", ans);
return 0;
}
单纯就是考数学,从下标 1 开始存储 n 天的公里数在数组 a 中,对 n 个数据从大到小排序,i 表示了骑车的天数,那么满足a[i] > i
的最大值即答案
#include
#include
using namespace std;
int getFriendNum(int num)
{
int sum = 0;
while(num != 0)
{
sum += num % 10;
num /= 10;
}
return sum;
}
int main()
{
set<int> s;
int n, num;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &num);
s.insert(getFriendNum(num));
}
printf("%d\n", s.size());
set<int>::iterator it; //迭代器
for(it = s.begin(); it != s.end(); it++)
{
if(it != s.begin()) printf(" "); //与 firstblood神似
printf("%d", *it);
}
return 0;
}
题目和代码本身不难,但补充几个有关 set 的小知识点:
//1 定义时不用写集合个数,因为不常用到下标
set<int> s;
//2 关于 int型的元素会自动排序,不用手动 sort
//3 输出 set元素不用下标法,用如下方法
for(auto it = s.begin(); it != s.end(); it++) //auto 非常好用,但只适用于 PAT
set<int>::iterator it; //迭代器
for(it = s.begin(); it != s.end(); it++) //蓝桥杯和 PAT都适用
//也是关于蓝桥杯和 PAT的
stoi()
//此函数仅适用于 PAT
string str = "010";
int a = stoi(str, 0, 2); //将 2进制转换成 10进制
int b = stoi(str, 0, 3); //将 3进制转换成 10进制
cout << a << endl;
cout << b << endl;
//中间的参数是起始位置,但最好别改(改了会报错,我也不知道怎么回事)
output:
2
3
#include
using namespace std;
int main()
{
string password, temp;
int n, cnt = 0;
cin >> password >> n;
getchar(); //当后面有 getline时必要的操作
while(1)
{
getline(cin, temp);
if (temp == "#") break;
cnt++;
if (cnt <= n && temp == password)
{
cout << "Welcome in";
break;
}
else if (cnt <= n && temp != password)
{
cout << "Wrong password: " << temp << endl;
if (cnt == n)
{
cout << "Account locked";
break;
}
}
}
return 0;
}
题目和代码本身简单,但想回顾一下字符串的边界与 getchar()的必要性
#include
#include
#include
using namespace std;
int m, n, tol;
vector<vector<int> > v;
int dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
bool judge(int i, int j)
{
for (int k = 0; k < 8; k++) //八种情况
{
int tx = i + dir[k][0];
int ty = j + dir[k][1];
if (tx >= 0 && tx < n && ty >= 0 && ty < m && v[i][j] - v[tx][ty] >= 0 - tol && v[i][j] - v[tx][ty] <= tol) return false;
}
return true;
}
int main()
{
int cnt = 0, x = 0, y = 0;
scanf("%d%d%d", &m, &n, &tol); //横坐标 纵坐标 差值
v.resize(n, vector<int>(m));
map<int, int> mapp;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
scanf("%d", &v[i][j]);
mapp[v[i][j]]++;
}
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (mapp[v[i][j]] == 1 && judge(i, j) == true)
{
cnt++;
x = i + 1;
y = j + 1;
}
}
}
if (cnt == 1)
printf("(%d, %d): %d", y, x, v[x-1][y-1]);
else if (cnt == 0)
printf("Not Exist");
else
printf("Not Unique");
return 0;
}
分析:思路大致和迷宫问题类似,但此题有个额外注意的地方,即判定条件,仅当满足在 m坐标 与 n坐标内 和 差值在200之间才判定为否,其他均为正确,这样做的好处就是判断越界的元素也是对的(相当于在边界的元素只用判定相邻的元素即可)
补充:vector 二维数组的两种表达方式,举例如下:
int n=5,m=5;
//第一种
vector<vector<int> > v(n, vector<int>(m));
//第二种 (安全些,不容易发生段错误)
vector<vector<int> > v;
v.resize(n, vector<int>(m));
额外:vector 的 resize 另一种用法
补充:有关 map 的用法,举例如下:
map<int,int> m;
m[1000]++;
m[100]++;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++)
cout << (*it).first << " " << (*it).second << endl;
//说明 map可直接用来存储关联数,并可以有效提取
output:
100 1
1000 1
m[1]=5,m[2]=4,m[3]=3,m[4]=2,m[5]=1;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++){
cout << (*it).first << " " << (*it).second << endl;
//再一次论证
output:
1 5
2 4
3 3
4 2
5 1
#include
#include
using namespace std;
int main()
{
int m, n, s;
scanf("%d%d%d", &m, &n, &s); //转发的总量 中奖间隔 第一位中奖者序号
string str;
map<string, int> mapp; //新用法
bool flag = false;
for (int i = 1; i <= m; i++)
{
cin >> str;
if (mapp[str] == 1) s = s + 1; //写在循环前面
if (i == s && mapp[str] == 0)
{
mapp[str] = 1;
cout << str << endl;
flag = true;
s = s + n;
}
}
if (flag == false) cout << "Keep going...";
return 0;
}
这个代码很机智的将复杂简化了,若是一般写容易i+n
,但本代码将全部循环写出来,并用另一个变量 s 代替 + n,这样就避免代码复杂化
#include
#include
#include
using namespace std;
int main()
{
int n;
scanf("%d", &n);
vector<int> v(n);
for (int i = 0; i < n; i++)
scanf("%d", &v[i]);
sort(v.begin(), v.end());
int result = v[0];
for (int i = 1; i < n; i++)
result = (result + v[i]) / 2;
printf("%d", result);
return 0;
}
数学题,因为所有长度都要串在一起,所以越早串绳子,越要对折的次数多,所以既然希望绳子长度是最长的,就必须让长的段对折次数尽可能的短
#include
#include
#include
#include
using namespace std;
int main()
{
int n, m, optnum, truenum, temp, maxcnt = 0;
int hash[] = {1, 2, 4, 8, 16}, opt[1005][105] = {0}; //每一个选项 学生选项
char c;
scanf("%d %d", &n, &m); //学生人数 多选题的个数
vector<int> fullscore(m), trueopt(m); //满分值 正确选项
vector<vector<int> > cnt(m, vector<int>(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++)
{
scanf("\n");
scanf("(%d", &temp);
for (int k = 0; k < temp; k++)
{
scanf(" %c", &c);
opt[i][j] += hash[c-'a'];
}
scanf(")", &c);
int el = opt[i][j] ^ trueopt[j]; //异或
if (el)
{
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
grade += fullscore[j];
}
printf("%.1f\n", grade); //学生得分
}
for (int i = 0; i < m; i++)
for (int j = 0; j < 5; j++)
maxcnt = max(maxcnt,cnt[i][j]); //最大错误次数
if (maxcnt == 0)
printf("Too simple\n");
else
for (int i = 0; i < m; i++)
for (int j = 0; j < 5; j++)
if (maxcnt == cnt[i][j])
printf("%d %d-%c\n", maxcnt, i+1, 'a'+j); //错误次数 题目编号 选项(字母)
return 0;
}
分析:用异或运算来判断一个选项和正确选项是否匹配,如果是匹配的,那么异或的结果应当是0,如果不匹配,那么这个选项就是存在错选或者漏选的情况,例如:设 a 为 00001,b 为 00010,c 为 00100,d 为 01000,e 为 10000,如果给定的正确答案是 ac,即 10001,那么如果给出的选项也是 10001,异或的结果就是 0,如果给出的选项是 a(漏选)或者 ab(选错),即 00001 或 00011,异或之后皆不为 0,通过 异或操作的结果 用 与运算符 就可以把错选和漏选的选项找出来,本代码 存储选项的变量 存储的是二进制值,二进制由 hash 给出分别是1 2 4 8 16,即二进制各位置的 1(本题需要设置的变量特别多)
补充:
运算符 | 描述 |
---|---|
& | 按位与运算符: 参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0 |
| | 按位或运算符: 只要对应的二个二进位有一个为1时,结果位就为1。 |
^ | 按位异或运算符: 当两对应的二进位相异时,结果为1 |
~ | 按位取反运算符: 对数据的每个二进制位取反,即把1变为0,把0变为1 ;~x 类似于 -x-1 |
<< | 左移动运算符: 运算数的各二进位全部左移若干位,由 << 右边的数字指定了移动的位数,高位丢弃,低位补0。 |
>> | 右移动运算符: 把">>"左边的运算数的各二进位全部右移若干位,>> 右边的数字指定了移动的位数 |
下面以变量 a 为 60,b 为 13 举例,二进制格式如下:
a = 0011 1100
b = 0000 1101
-----------------
a & b = 0000 1100
a | b = 0011 1101
a ^ b = 0011 0001
~a = 1100 0011
a << 2 = 1111 0000
a >> 2 = 0000 1111
#include
using namespace std;
int main()
{
string s, s1, s2, ans;
int carry = 0, flag = 0;
cin >> s >> s1 >> s2;
ans = s;
string ss1(s.length() - s1.length(), '0'); //长度补齐
s1 = ss1 + s1;
string ss2(s.length() - s2.length(), '0'); //长度补齐
s2 = ss2 + s2;
for(int i = s.length() - 1; i >= 0; i--)
{
int mod = s[i] == '0' ? 10 : (s[i] - '0');
ans[i] = (s1[i] - '0' + s2[i] - '0' + carry) % mod + '0'; //当前位
carry = (s1[i] - '0' + s2[i] - '0' + carry) / mod; //进制位
}
if (carry != 0) ans = '1' + ans;
for(int i = 0; i < ans.size(); i++)
{
if (ans[i] != '0' || flag == 1 || carry != 0)
{
flag = 1;
cout << ans[i];
}
}
if (flag == 0) cout << 0;
return 0;
}
代码非常简洁,但没什么好讲的,回顾一下 string 类进制
#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);
}
while(start != -1)
{
int data = list[start].data;
if (data < 0) //在选择的类别里面尾插
v[0].push_back(start);
else if (data >= 0 && data <= k)
v[1].push_back(start);
else
v[2].push_back(start);
start = list[start].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;
}
此题与1025类似,但那题用的是堆数组,而此题用的是 vector 数组,作用就是可以根据题目要求分三类,而要插入直接 push_back,非常的简洁(那题只用分一类,不用多此一举)
#include
using namespace std;
int main()
{
string s;
while (cin >> s)
if(s.size() == 3 && s[2] == 'T') cout << s[0]-'A'+1;
return 0;
}
分析:超精简代码,n 没什么作用,以字符串方式接收输入,只要遇到任何一个字符串 s 满足大小为 3 且 s[2] 为 ‘T’,就将 s[0] 字母对应的 wifi 密码输出
#include
#include
#include
#include
using namespace std;
struct node
{
string name;
int gp, gm, gf, g; //编程 期中 期末 总评
};
bool cmp(node a, node b)
{
return a.g != b.g ? a.g > b.g : a.name < b.name;
}
map<string, int> idx; //用于记录排除编程后的结构体顺序
int main()
{
int p, m, n, score, cnt = 1;
cin >> p >> m >> n;
vector<node> v, ans; //初始动态结构体数组 最终动态结构体数组
string s;
for (int i = 0; i < p; i++)
{
cin >> s >> score;
if (score >= 200) //先实施排除编程
{
v.push_back(node{s, score, -1, -1, 0}); //尾插结构体
idx[s] = cnt++;
}
}
for (int i = 0; i < m; i++)
{
cin >> s >> score;
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(v[temp].gm * 0.4 + v[temp].gf * 0.6 + 0.5); //总评变化
}
}
for (int i = 0; i < v.size(); i++)
if (v[i].g >= 60) ans.push_back(v[i]); //最后实施排除总评
sort(ans.begin(), ans.end(), cmp);
for (int i = 0; i < ans.size(); i++)
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;
}
分析:
1、因为所有人必须要G编程>=200分,所以用v数组保存所有G编程>=200的人,(一开始gm和gf都为-1),用map映射保存名字所对应v中的下标(为了避免与“不存在”混淆,保存下标+1,当为0时表示该学生的姓名在v中不存在)
2、G期中中出现的名字,如果对应的map并不存在(==0),说明该学生编程成绩不满足条件,则无须保存该学生信息。将存在的人的期中考试成绩更新
3、G期末中出现的名字,也必须保证在map中存在,先更新G期末和G总为新的成绩,当G期末
#include
using namespace std;
int main()
{
string s;
int n, j;
cin >> s >> n;
for (int cnt = 1; cnt < n; cnt++)
{
string t;
for (int i = 0; i < s.length(); i = j)
{
for (j = i; j < s.length() && s[j] == s[i]; j++); //注意是让 for循环单独循环下去
t += s[i] + to_string(j - i);
}
s = t;
}
cout << s;
return 0;
}
分析:题目的意思是 d 代表某个数字来进行数列演示,而关于代码的关键部分,可以自行想象数字(字符串)迭代的过程
补充:
to_string()函数作用:将数字常量转换成 string 类
注意:此函数在 PTA 可以使用,而蓝桥杯不允许
若要用可以使用代替的函数表示,举例如下:
string to_string(int a)
{
string b="";
char c[1000]={0}; //存储空间根据题目调整
sprintf(c,"%d",a);
for(int i=0;i<strlen(c);i++)
b+=c[i];
return b;
}
//注意还有头文件 和
#include
#include
#include
#include
#include
using namespace std;
struct node
{
string school;
int tws, ns; //分数 人数
};
bool cmp(node a, 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; //string 可以按 ASCII码排序
}
int main()
{
int n;
scanf("%d", &n);
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;
for (int j = 0; j < school.length(); j++)
school[j] = tolower(school[j]);
if (id[0] == 'B')
score = score / 1.5;
else if (id[0] == 'T')
score = score * 1.5;
sum[school] += score;
cnt[school]++;
}
vector<node> ans;
for (auto it = cnt.begin(); it != cnt.end(); it++)
ans.push_back(node{it->first, (int)sum[it->first], cnt[it->first]}); //新数组接收
sort(ans.begin(), ans.end(), cmp);
int rank = 0, pres = -1;
printf("%d\n", (int)ans.size());
for (int i = 0; i < ans.size(); i++)
{
if (pres != ans[i].tws) rank = i + 1; //处理并列排名的妙招
pres = ans[i].tws;
printf("%d ", rank);
cout << ans[i].school;
printf(" %d %d\n", ans[i].tws, ans[i].ns);
}
return 0;
}
分析:两个 map,一个 cnt 用来存储某学校名称对应的参赛人数,另一个 sum 计算某学校名称对应的总加权成绩,每次学校名称 string school 都要转化为全小写,将 map 中所有学校都保存在 vector ans 中,类型为 node,node 中包括学校姓名、加权总分、参赛人数,对 ans 数组排序,根据题目要求写好 cmp 函数,最后按要求输出,对于排名的处理:设立 pres 表示前一个学校的加权总分,如果 pres 和当前学校的加权总分不同,说明 rank 等于数组下标 + 1,否则 rank不变
补充:
本代码用的头文件 <unordered_map> 仅适用于 PTA
而蓝桥杯只用用 <map>
同理,蓝桥杯不能用 auto ,要转为迭代器类型
#include
#include
#include
using namespace std;
int main()
{
int a, b;
scanf("%d %d", &a, &b);
string s = to_string(a * b);
reverse(s.begin(), s.end());
printf("%d", stoi(s));
return 0;
}
单纯回顾一下 string 类 既有 to_string 又有 stoi 还可以 reverse
#include
#include
#include
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> v(n+1);
for (int i = 1; i <= n; i++) cin >> v[i]; //每个人说的话
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
vector<int> lie, a(n + 1, 1); //lie 数组代表说谎的人 ,a 数组代表真实情况 ,好人全赋值为 1
a[i] = a[j] = -1; //i j 为坏人 ,赋值为 -1
for (int k = 1; k <= n; k++)
if (v[k] * a[abs(v[k])] < 0) lie.push_back(k);
if (lie.size() == 2 && a[lie[0]] + a[lie[1]] == 0)
{
cout << i << " " << j;
return 0;
}
}
}
cout << "No Solution";
return 0;
}
分析:题目:已知 N 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎,说明这两个说谎的人一个是好人一个是狼人,每个人说的数字保存在 v 数组中,i 从 1~n - 1 、j 从 i + 1~n 遍历,分别假设 i 和 j 是狼人,a 数组表示真实情况(该人是狼人还是好人),等于 1 表示是好人,等于 -1 表示是狼人。k 从 1~n 分别判断 k 所说的话是真是假,k 说的话和真实情况不同(即v[k] * a[abs(v[k])] < 0
)则表示 k 在说谎,则将 k 放在 lie 数组中,遍历完成后判断 lie 数组,如果说谎人数等于 2 并且这两个说谎的人一个是好人一个是狼人(即a[lie[0]] + a[lie[1]] == 0
)表示满足题意,此时输出 i 和 j 并 return,否则最后的时候输出 No Solution
#include
#include
#include
using namespace std;
int main()
{
int n, k, t1, t2;
map<int,vector<int>> m; //map 的又一种用法
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) //map 不止一一对应
{
scanf("%d%d", &t1, &t2);
m[t1].push_back(t2);
m[t2].push_back(t1);
}
while (k--)
{
int cnt, flag = 0, a[100000] = {0};
scanf("%d", &cnt);
vector<int> v(cnt);
for (int i = 0; i < cnt; i++)
{
scanf("%d", &v[i]);
a[v[i]] = 1;
}
for (int i = 0; i < v.size(); i++)
for (int j = 0; j < m[v[i]].size(); j++)
if (a[m[v[i]][j]] == 1) flag = 1;
printf("%s\n",flag ? "No" :"Yes");
}
return 0;
}
分析:用 map 存储每一个货物的所有不兼容货物(用到了 map 的新用法),在判断给出的一堆货物是否是相容的时候,判断任一货物的不兼容货物是否在这堆货物中,如果存在不兼容的货物,则这堆货物不能相容,如果遍历完所有的货物,都找不到不兼容的两个货物,则这堆货物就是兼容的
#include
#include
using namespace std;
int main()
{
int m;
cin >> m;
while (m--)
{
int k, flag = 0;
cin >> k;
for (int n = 1; n < 10; n++) //枚举
{
int mul = n * k * k;
string smul = to_string(mul), sk = to_string(k);
string smulend = smul.substr(smul.length() - sk.length());
if (smulend == sk)
{
printf("%d %d\n", n, mul);
flag = 1;
break;
}
}
if (flag == 0) printf("No\n");
}
return 0;
}
题目和代码简单,解析一下 string 类的新用法:substr()函数
string temp;
temp = suf.substr(Off, Count)
off 参数: 复制子字符串的起始位置
Count 参数: 复制的字符数目
下面再举例说明:
string suf = "abcde";
string temp,temp2;
temp = suf.substr(2,1);
temp2 = suf.substr(2); //若没有第二个参数则代表一直复制到结尾
//下面实例看出是从 0开始计数
output:
c
cde
#include
using namespace std;
int main()
{
string s1, s2, s;
int hash[200] = {0};
getline(cin, s1);
getline(cin, s2);
s = s1 + s2;
for (int i = 0; i < s.size(); i++) //妙招 输出并剔除重复字符
{
if (hash[s[i]] == 0) cout << s[i];
hash[s[i]] = 1;
}
return 0;
}
分析:因为题目要求若有重复字符先出现 A 的,故做法为:A + B 在剔除重复字符
#include
#include
#include //更快(仅适用于 PTA)
#include
using namespace std;
struct node
{
string t; //1类准考证 3类考场号
int value; //1类成绩 3类考场人数
};
bool cmp(const node &a, const node &b) //用引用传参 (速度更快 养成好习惯)
{
return a.value != b.value ? a.value > b.value : a.t < b.t;
}
int main()
{
int n, k, num;
string s;
cin >> n >> k;
vector<node> v(n);
for (int i = 0; i < n; i++)
cin >> v[i].t >> v[i].value;
for (int i = 1; i <= k; i++)
{
cin >> num >> s;
printf("Case %d: %d %s\n", i, num, s.c_str());
vector<node> ans; //存储筛选后最终数组
int cnt = 0, sum = 0;
if (num == 1) //1类
{
for (int j = 0; j < n; j++)
if (v[j].t[0] == s[0]) ans.push_back(v[j]); //等级查询
}
else if (num == 2) //2类
{
for (int j = 0; j < n; j++)
{
if (v[j].t.substr(1, 3) == s) //考场查询
{
cnt++;
sum += v[j].value;
}
}
if (cnt != 0) printf("%d %d\n", cnt, sum);
}
else if (num == 3) //3类
{
unordered_map<string, int> m;
for (int j = 0; j < n; j++)
if (v[j].t.substr(4, 6) == s) m[v[j].t.substr(1, 3)]++; //考试日期 考场编号人数 ++
for (auto it : m) ans.push_back({it.first, it.second}); //用 it 遍历 容器 m中的每一个元素
}
sort(ans.begin(), ans.end(),cmp); //一个 cmp 全类型通用
for (int j = 0; j < ans.size(); j++)
printf("%s %d\n", ans[j].t.c_str(), ans[j].value);
if (((num == 1 || num == 3) && ans.size() == 0) || (num == 2 && cnt == 0)) printf("NA\n");
}
return 0;
}
分析:非常有参考价值的分类题
题目大意:给出一组学生的准考证号和成绩,准考证号包含了等级(乙甲顶),考场号,日期,和个人编号信息,并有三种查询方式
查询一:给出考试等级,找出该等级的考生,按照成绩降序,准考证升序排序
查询二:给出考场号,统计该考场的考生数量和总得分
查询三:给出考试日期,查询改日期下所有考场的考试人数,按照人数降序,考场号升序排序
代码:先把所有考生的准考证和分数记录下来
1、按照等级查询,枚举选取匹配的学生,然后排序即可
2、按照考场查询,枚举选取匹配的学生,然后计数、求和
3、按日期查询每个考场人数,用 unordered_map 存储,最后排序汇总
注意:1、第三个用 map 存储会超时,用unordered_map就不会超时啦
2、排序传参建议用引用传参,这样更快,虽然有时候不用引用传参也能通过,但还是尽量用,养成好习惯
解析:c_str() 函数的用法
作用:将 C++ 的 string 转化为 C 的字符串数组输出
string temp = "123";
printf("%s",temp.c_str());
//这样就可以将 temp 以 c语言 的形式输出(目的是为了快)
output:123
补充:for (auto it : m) 函数的用法
c++11的新特性,范围 for
m 是一个可遍历的容器或流,比如 vector 类型
it 就用来在遍历过程中获得容器里的每一个元素
vector<int> m={1,2,3,4};
for(auto it : v)
cout<<it;
output: 1234
//注意仅 PTA 适用,蓝桥杯不允许