第四章 入门篇2
4.1排序
PAT B1015 德才论
有三个测试点没有通过,显示段错误,重新读了一遍题目发现自己的数组并没有越界,都比题目给定的范围要大,为什么会出现这个问题?
估计问题的原因是占用内存太多(?!),因为开了五个N的结构体数组。书中给出的算法很简略,主要思路是在student结构体中增加一类来标记分类,1,2,3,4,5这样的方式可以在输入完成之后直接分类即可,只需要把比较函数第一个return 写成a.classes<b.classes即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Student{
char id[10];
int d_score, c_score;
int total_score;
};
bool cmp(Student a, Student b) {
if(a.total_score!=b.total_score) return a.total_score>b.total_score;
else if(a.d_score!=b.d_score) return a.d_score>b.d_score;
else return strcmp(a.id, b.id)<0;
}
int main() {
int N, L, H;
scanf("%d%d%d", &N, &L, &H);
Student info_a[N], info_b[N], info_c[N], info_d[N]; //分别表示四种人
Student info[N];
int j=0, k=0, l=0, m=0;
for(int i=0; i<N; i++) { //读入学生信息,并分成四类人
scanf("%s %d %d", info[i].id, &info[i].d_score, &info[i].c_score);
info[i].total_score=info[i].d_score+info[i].c_score;
if(info[i].d_score>=H && info[i].c_score>=H) {
info_a[j++]=info[i];
} else if(info[i].d_score>=H && info[i].c_score<H && info[i].c_score>=L) {
info_b[k++]=info[i];
} else if(info[i].d_score<H && info[i].c_score<H && info[i].d_score>=info[i].c_score && info[i].c_score>=L) {
info_c[l++]=info[i];
} else if(info[i].d_score>=L && info[i].c_score>=L){
info_d[m++]=info[i];
}
}
int M=j+k+l+m; //所有达到分数线的人数
sort(info_a, info_a+j, cmp); //将四类人分别排序
sort(info_b, info_b+k, cmp);
sort(info_c, info_c+l, cmp);
sort(info_d, info_d+m, cmp);
printf("%d\n", M);
if(M==0) exit(0);
for(int i=0; i<j; i++) {
printf("%s %d %d\n", info_a[i].id, info_a[i].d_score, info_a[i].c_score);
}
for(int i=0; i<k; i++) {
printf("%s %d %d\n", info_b[i].id, info_b[i].d_score, info_b[i].c_score);
}
for(int i=0; i<l; i++) {
printf("%s %d %d\n", info_c[i].id, info_c[i].d_score, info_c[i].c_score);
}
for(int i=0; i<m; i++) {
printf("%s %d %d\n", info_d[i].id, info_d[i].d_score, info_d[i].c_score);
}
return 0;
}
已通过OJ
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Student{
char id[10];
int d_score, c_score;
int total_score;
int classes; //1,2,3,4,5分别表示五类人
};
bool cmp(Student a, Student b) {
if(a.classes!=b.classes) return a.classes<b.classes;
else if(a.total_score!=b.total_score) return a.total_score>b.total_score;
else if(a.d_score!=b.d_score) return a.d_score>b.d_score;
else return strcmp(a.id, b.id)<0;
}
int main() {
int N, L, H;
scanf("%d%d%d", &N, &L, &H);
printf("here21\n");
Student info[N];
for(int i=0; i<N; i++) { //读入学生信息,并分成五类人
scanf("%s %d %d", info[i].id, &info[i].d_score, &info[i].c_score);
info[i].total_score=info[i].d_score+info[i].c_score;
if(info[i].d_score>=H && info[i].c_score>=H) {
info[i].classes=1;
} else if(info[i].d_score>=H && info[i].c_score<H && info[i].c_score>=L) {
info[i].classes=2;
} else if(info[i].d_score<H && info[i].c_score<H && info[i].d_score>=info[i].c_score && info[i].c_score>=L) {
info[i].classes=3;
} else if(info[i].d_score>=L && info[i].c_score>=L){
info[i].classes=4;
} else {
info[i].classes=5;
}
}
sort(info, info+N, cmp);
int m=0; //表示总的过线人数
for(int i=0; i<N; i++) {
if(info[i].classes!=5) {
m++;
}
}
printf("%d\n", m);
for(int i=0; i<m; i++) {
printf("%s %d %d\n", info[i].id, info[i].d_score, info[i].c_score);
}
return 0;
}
4.2 散列(hash)
B1029 旧键盘
注意:PAT已经不再支持gets()读入字符串了,必须使用cin.getline()来输入一整行的字符串
总结:1.对于需要在一个字符串中寻找在另外一个字符串中是否出现及出现次数这种题目时,考虑用hashtable, 一般对于字母,数字,等定义hashtable为128即可,即使用符号的ASCII码作为下标来读取。
2.对于cin:
#include <iostream>
using namespace std;
//cin.getline(a1, 100); 这样的格式才可以,不能少了字符数组的大小!
已通过OJ
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
bool hashtable[128]={false};
char a1[90], a2[90];
int p1=0, p2=0;
cin.getline(a1, 90);
cin.getline(a2, 90);
for(int i=0; i<strlen(a1); i++) {
if(a1[i]>='a' && a1[i]<='z') {
a1[i] = a1[i]-'a'+'A';
}
int j;
for(j=0; j<strlen(a2); j++) {
if(a2[j]>='a' && a2[j]<='z') {
a2[j] = a2[j]-'a'+'A';
}
if(a1[i]==a2[j]) {
break;
}
}
if(j==strlen(a2)) {
if(hashtable[a1[i]]==false) {
printf("%c", a1[i]);
hashtable[a1[i]]=true;
}
}
}
return 0;
}
B 1033 旧键盘打字
问题:1.数组的最大范围设置少了一个0,注意题目说的是小于等于105 ,因此maxsize应该不小于105 +1, 令maxsize=100010即可。
2.没看清楚题目,题目明确说了输入的坏键以大写字母表示!先把逻辑关系缕清在动手写代码,不要着急按。
3.本题可以先把坏键中的大写字母全部转化为小写字母,并令hashtable[]对应的位置为false(初值设为true),在遍历第二个数组的时候,
每遍历一个字符,就把其转化为小写字母(如果是英文大写字母的话),然后在hashtable[low]==true && hash[’+]==true的同时才可以把这个字母打印出来。
....
bool hashtable[256]={true};
for(int i=0; i<len1; i++) {
if(a1[i]>='A' && a1[i]<='Z') {
a1[i] += 32;
}
}
for(int i=0; i<len2; i++) {
if(a2[i]>='A' && a2[i]<='Z') {
int low=a2[i]+32;
if(hashtable[low]==true && hashtable['+']==true) {
printf("%c", a2[i]);
}
} else if(hashtable[a2[i]]==true) {
printf("%c", a2[i]);
}
}
....
已通过OJ
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxsize=100010;
int main() {
bool hashtable[256]={false};
char a1[maxsize], a2[maxsize];
cin.getline(a1, maxsize);
cin.getline(a2, maxsize);
int len1=strlen(a1);
int len2=strlen(a2);
bool wrong_big=true; //表示上档键
for(int i=0; i<len1; i++) {
if(a1[i]=='+') {
wrong_big=false;
}
}
if(wrong_big==false) {
for(int i=65; i<=90; i++) {
hashtable[i]=true;
}
}
for(int i=0; i<len1; i++) { //为true表示坏掉的键盘对应的键
hashtable[a1[i]]=true;
if(a1[i]>='A' && a1[i] <='Z') {
hashtable[a1[i]+32]=true;
}
}
for(int i=0; i<len2; i++) {
if(hashtable[a2[i]]==false) {
printf("%c", a2[i]);
}
}
printf("\n");
return 0;
}
B1038
已通过OJ
#include <cstdio>
int main() {
int hashtable[101]={0};
int n, grade;
scanf("%d", &n);
while(n--) { //读入数据
scanf("%d", &grade);
hashtable[grade]++;
}
int k;
scanf("%d", &k);
int query[k];
for(int i=0; i<k; i++) { //读入待查询的学生分数
scanf("%d", &query[i]);
}
for(int i=0; i<k; i++) {
printf("%d", hashtable[query[i]]);
if(i<k-1) printf(" ");
}
printf("\n");
return 0;
}
B1039 到底买不买
已通过OJ
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxsize=1010;
int main() {
char a1[maxsize];
char a2[maxsize];
int hashtable1[128]={0}, hashtable2[128]={0};
cin.getline(a1, maxsize);
cin.getline(a2, maxsize);
for(int i=0; i<strlen(a1); i++) {
hashtable1[a1[i]]++;
}
for(int i=0; i<strlen(a2); i++) {
hashtable2[a2[i]]++;
}
int more=0, less=0;
bool flag=true; //true表示足够,false表示不够
for(int i=0; i<128; i++) {
if(hashtable2[i]<=hashtable1[i] && flag==true) {
more += hashtable1[i]-hashtable2[i];
} else if(hashtable2[i]>hashtable1[i]){
less += hashtable2[i]-hashtable1[i];
flag=false;
}
}
if(flag==true) {
printf("Yes %d\n", more);
} else {
printf("No %d\n", less);
}
return 0;
}
以下为书中代码,更加简洁,思路也更清晰。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxsize=1010;
int hashtable[80], miss=0;
int change(char c) {
if(c>='0' && c<='9') return c-'0';
else if(c>='A' && c<='Z') return c-'A'+10;
else if(c>='a' && c<='z') return c-'a'+36;
}
int main() {
char whole[maxsize], target[maxsize];
cin.getline(whole, maxsize);
cin.getline(target, maxsize);
int len1=strlen(whole);
int len2=strlen(target);
for(int i=0; i<len1; i++) {
int id=change(whole[i]);
hashtable[id]++;
}
for(int i=0; i<len2; i++) {
int id=change(target[i]);
hashtable[id]--;
if(hashtable[id]<0) {
miss++;
}
}
if(miss>0) {
printf("No %d\n", miss);
} else {
printf("Yes %d\n", len1-len2);
}
return 0;
}
B 1042 字符统计
已通过OJ
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxsize=1010;
int hashtable[256]={0};
int main() {
char a1[maxsize];
cin.getline(a1, maxsize);
int len1=strlen(a1);
for(int i=0; i<len1; i++) {
if(a1[i]>='A' && a1[i]<='Z') {
hashtable[a1[i]+32] ++;
} else {
hashtable[a1[i]]++;
}
}
int max=97; //初始时a的位序,不妨令其为最大值
for(int i=97; i<=122; i++) {
if(hashtable[i]>hashtable[max]) {
max=i;
}
}
printf("%c %d\n", max, hashtable[max]);
return 0;
}
总结:1.由于题目要求只输出出现频率最大的字母,因此可以只保存字母出现的频率,又小写字母只有26个,hashtable的size只要大于26即可。
B 1043输出PATest
已通过OJ
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxsize=10010;
char target[6]={'P', 'A', 'T', 'e', 's', 't'};
int hashtable[6]={0};
int main() {
char a1[maxsize];
cin.getline(a1, maxsize);
int len1=strlen(a1);
int count=0; //表示这六个字符出现的总次数
for(int i=0; i<len1; i++) {
int temp=-1;
for(int j=0; j<6; j++) {
if(a1[i]==target[j]) {
temp=j;
count++;
}
}
if(temp>-1) {
hashtable[temp]++;
}
}
while(count>0) {
for(int i=0; i<6; i++) {
if(hashtable[i]>0) {
printf("%c", target[i]);
hashtable[i]--;
count--;
}
}
}
printf("\n");
return 0;
}
B 1047编程团体赛----这题也太简单了吧
已通过OJ
#include <cstdio>
int hashtable[1010]={0};
int n;
int main() {
scanf("%d", &n);
int queue, member, score;
while(n--) {
scanf("%d-%d %d", &queue, &member, &score);
hashtable[queue] += score;
}
int max=0; //表示成绩最高的队伍的编号
for(int i=1; i<1010; i++) {
if(hashtable[i]>hashtable[max]) {
max=i;
}
}
printf("%d %d\n", max, hashtable[max]);
return 0;
}
B 1005 继续(3n+1)猜想
问题:1.有一个测试点没通过,显示段错误,一开始以为是hashtable开小了,也确实小了,但是当开到300,500的时候还是显示段错误,且有新的测试点也显示段错误。
建议直接开到10,000 以上
已通过OJ
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxsize=110;
int a[maxsize];
int hashtable[10000]={0};
bool cmp(int a, int b) {
return a>b;
}
int main() {
int k;
scanf("%d", &k);
for(int i=0; i<k; i++) {
scanf("%d", &a[i]);
int t=a[i];
while(t!=1 && (hashtable[t]<=1)) {
hashtable[t]++;
if(t%2==0) {
t /=2;
} else {
t =(3*t+1)/2;
}
}
}
sort(a, a+maxsize, cmp);
int count=0;
for(int i=0; i<k; i++) {
if(hashtable[a[i]]==1) {
count++;
}
}
for(int i=0; i<k; i++) {
if(hashtable[a[i]]==1) {
printf("%d", a[i]);
count--;
if(count>0) printf(" ");
}
}
return 0;
}
PAT B 1020 月饼
问题:有一个测试点显示错误。
原因 :题目中的库存量及总售价可以是浮点数!,因此可以把struct中的所有域都定义为double类型!
已通过OJ
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxsize=1010;
struct Bisguit {
double sum, total_price;
double price;
}info[maxsize];
bool cmp(Bisguit a, Bisguit b) {
return a.price>b.price;
}
int main() {
int n;
double m;
scanf("%d%lf", &n, &m);
for(int i=0; i<n; i++) {
scanf("%lf", &info[i].sum);
}
for(int i=0; i<n; i++) {
scanf("%lf", &info[i].total_price);
}
for(int i=0; i<n; i++) {
info[i].price=1.0*info[i].total_price/info[i].sum;
}
sort(info, info+n, cmp);
double benefit=0;
for(int i=0; i<n; i++) {
if(m-info[i].sum>=0) {
benefit += info[i].total_price;
m -= info[i].sum;
} else {
benefit += m*info[i].price;
break;
}
}
printf("%.2f\n", benefit);
return 0;
}
PAT B1023 组个最小数
#include <cstdio>
int hashtable[10]={0};
int main() {
int min=10; //表示第一个不是零的数
for(int i=0; i<10; i++) {
scanf("%d", &hashtable[i]);
if(hashtable[i]>0 && i<min &&i>0) {
min=i;
}
}
printf("%d", min);
hashtable[min]--;
for(int i=0; i<10; i++) {
while(hashtable[i]>0) {
printf("%d", i);
hashtable[i]--;
}
}
printf("\n");
return 0;
}
A 1038
总结:1.关于string容器的使用,见 链接?
2.本题思路:使用贪心的基本思考方式,对于数字串s1,s2,如果s1+s2<s2+s1 ,则把s1放在s2前面,否则反过来。这种策略的实现可以通过string的operate += 及>,<…等操作实现,这样得到的序列最后肯定是最小的。
3.关于前导零的消除,可以使用string.erase(),但要注意一个边界情况,就是当所有的数字都是零的时候,直接输出0,这个特殊情况直接特例写出来即可。
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=10010;
string str[maxn];
bool cmp(string a, string b) {
return a+b<b+a;
}
int main() {
int n;
scanf("%d", &n);
for(int i=0; i<n; i++) {
cin >> str[i];
}
sort(str,str+n, cmp);
string ans;
for(int i=0; i<n; i++) {
ans += str[i];
}
while(ans.size()!=0 && ans[0]=='0') {
ans.erase(ans.begin());
}
if(ans.size()==0) {
cout << "0" << endl;
} else {
cout << ans << endl;
}
return 0;
}
4.5 二分
B1030完美数列
思路:1.将问题转化为>>>>在递增序列中选择连续的一串数字满足该条件,因此只需要对每个数字顺序遍历一遍,找到最大串,即为答案。
2.如何找到使得min*p>=max的这样的一串,即如何确定上下标:采用二分的思维,对于第i个数字,片段从i+1到n-1查看其中满足条件的数。注意ans最少是1,所以从i+1开始,这样问题就转为了给定[i+1, n-1]找到数组这一段中满足a[k]>=a[i]*p的最后一个元素,即a[k]<a[i]*p的第一个元素位置,之后只要减一即可得到正确答案
3.函数返回j,从i到j-1的数字个数为 j-1-i+1=j-i
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
int n;
int binarysearch(int a[], int i, long long x) {
if(a[n-1]<=x) return n; //边界情况
int l=i+1, r=n-1,mid;
while(l<r) {
mid=(l+r)/2;
if(a[mid]>x) {
r=mid;
} else {
l=mid+1;
}
}
return r;
}
int main() {
int p;
scanf("%d%d", &n, &p);
int a[n];
for(int i=0; i<n; i++) {
scanf("%d", &a[i]);
}
sort(a, a+n);
int ans=1;
for(int i=0; i<n; i++) {
int j=binarysearch(a, i, (long long)a[i]*p);
if(ans<j-i) {
ans=j-i;
}
}
printf("%d\n", ans);
return 0;
}
B1003
问题:1.题目给定的条件没读懂,题目给的条件并不是那么简单的给出定义 ,还要求能推导出条件2是条件3的根条件,即条件3一定是从条件2推导出来的。
2.因此:可以考虑记录p前面的所有A, p与T中间的A,P后面的所有A,
.总的来说,这道题很坑,20分这么难
以下为书中代码
#include <cstdio>
#include <cstring>
int main() {
int T;
scanf("%d", &T);
while(T--) {
char str[110];
scanf("%s", str);
int len=strlen(str);
int num_p=0, num_t=0, other=0;
int loc_p=-1, loc_t=-1; //表示p和t的位置
for(int i=0; i<len; i++) {
if(str[i]=='P') {
num_p++;
loc_p=i;
} else if(str[i]=='T') {
num_t++;
loc_t=i;
} else if(str[i]!='A') {
other++;
}
}
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) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
B 1013
问题:1.输出的时候忘记了最后一个元素的结尾是不需要空格的!
已通过OJ
注释掉的是自己写的暴力法
埃氏法是书中代码,很简洁了!
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
//const int maxn=10010;
//long long prime[maxn], pNum=0;
//暴力筛素数
//bool isPrime(long long a) {
// if(a<=1) return false;
// long long sqr=(long long) sqrt(a);
// for(long long i=2; i<=sqr; i++) {
// if(a%i==0) return false;
// }
// return true;
//}
//
//void range_prime(int Na, int Nb) {
// int count=0;
// int i=2;
// while(1){
// if(isPrime(i)==true) {
// count++;
// if(count>=Na && count<=Nb) {
// prime[pNum++]=i;
// } else if(count>Nb) {
// break;
// }
// }
// i++;
// }
//}
//埃氏筛法
const int maxn=1000010;
bool p[maxn];
int prime[maxn], num=0;
void Find_Prime(int n) {
for(int i=2; i<maxn; i++) {
if(p[i]==false) {
prime[num++]=i;
if(num>=n) break;
for(int j=i+i; j<maxn; j+=i) {
p[j]=true;
}
}
}
}
int main() {
int Na, Nb, counts=0;
scanf("%d%d",&Na, &Nb);
//range_prime(Na, Nb);
Find_Prime(Nb);
for(int i=Na; i<=Nb; i++) {
printf("%d", prime[i-1]);
counts++;
if(counts%10!=0 && i<Nb) printf(" ");
else printf("\n");
}
// if(Nb<1) return 0;
// else {
// for(int i=0; i<pNum; i++) {
// if(counts<9) {
// printf("%lld", prime[i]);
// if(i!=pNum-1) printf(" ");
// counts++;
// } else if(counts==9) {
// printf("%lld\n", prime[i]);
// counts=0;
// }
// }
// }
return 0;
}