1085. PAT单位排行
题目分析:
这道题某些思想和1080很像,为了快速查找相同学校,我们可以先对整个数组进行排序,按学校的校名字典序升序排序,这样相同的校名就会在一起,就可以方便地统计人数以及总分。这里我的代码可能看起来有些绕,因为最开始有两个点超时了,放下了过了几天才回头改,有些变量名很相似,不容易区分。
源代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
struct grades{
char schID[7];
double totScore=0.0;
int weight_tot,number=0;
};
struct info{
char id[7];
int score;
char schName[7];
};
void update(const info& p,grades& g){
char rank=p.id[0];
if(rank=='A') g.totScore+=p.score;
else if(rank=='B') g.totScore+=p.score/1.5;
else g.totScore+=p.score*1.5;
g.number++; //人数+1
}
bool cmp1(const info& p1,const info& p2){
return strcmp(p1.schName,p2.schName)<0; //按校名字典升序排列
}
bool cmp2(const grades&t1,const grades& t2){
if(t1.weight_tot!=t2.weight_tot) return t1.weight_tot>t2.weight_tot;
if(t1.number!=t2.number) return t1.number<t2.number; //人数升序输出
return strcmp(t1.schID,t2.schID)<0; //按字典升序排列
}
int main(){
int num;
scanf("%d",&num);
grades* list=new grades[num];
info *infoList=new info[num];
for(int i=0;i<num;++i){
scanf("%s %d %s",infoList[i].id,&infoList[i].score,infoList[i].schName);
int len=strlen(infoList[i].schName);
for(int j=0;j<len;++j)
infoList[i].schName[j]=tolower(infoList[i].schName[j]); //转换成小写
}
std::sort(infoList,infoList+num,cmp1);
int count=0;//不同学校的数目,实际上会少1
char prename[7];
strcpy(prename,infoList[0].schName);
strcpy(list[count].schID,prename);
update(infoList[0],list[count]);
for(int i=1;i<num;++i){
if(strcmp(infoList[i].schName,prename)!=0){//发现新学校
strcpy(list[++count].schID,infoList[i].schName);
strcpy(prename,infoList[i].schName);
}
update(infoList[i],list[count]);
}
count++; //这样 count就表示了不同学校的数目
for(int i=0;i<count;++i)
list[i].weight_tot=list[i].totScore; //加权平均后保留整数部分
std::sort(list,list+count,cmp2);
printf("%d\n",count);
int preOrder=1;
printf("%d %s %d %d\n",preOrder,list[0].schID,list[0].weight_tot,list[0].number);
for(int i=1;i<count;++i){
if(list[i].weight_tot<list[i-1].weight_tot) preOrder=i+1; //非并列时
printf("%d %s %d %d\n",preOrder,list[i].schID,list[i].weight_tot,list[i].number);
}
delete []infoList;
delete []list;
return 0;
}
1086. 就不告诉你
题目分析:
(题目的图片我感觉很熟悉啊,是不是放牛班的春天里的?)
题目给的数据范围乘法完全不会溢出可以安心用int,倒着输出结果也不麻烦,用一个数组从低到高储存各位就好了,不过在输出时记得要先找到非零的第一项再开始输出。
源代码
#include <cstdio>
int main()
{
int n1,n2,mul,count=0;
int result[7]; //不超过7位数
scanf("%d %d",&n1,&n2);
mul=n1*n2;
while(mul){
result[count++]=mul%10;
mul/=10;
}
int j=0;
while(result[j]==0) j++; //找到第一个非零项
for(int i=j;i<count;++i)
printf("%d",result[i]);
return 0;
}
1087. 有多少不同的值
题目分析:
首先计算出给定值能算出来的最大情况,设为MAX吧,开一个大小为MAX的int数组来储存各个值是否出现。
再遍历一遍,把非零的元素对应的下标输出出来就可以了。
源代码
#include <cstdio>
int main()
{
int num;
scanf("%d",&num);
int upperBound=num*31/30; //至多出现不同值的可能性个数
int *valueList=new int[upperBound+1]; //由于不使用0,所以开辟数组大小要+1
for(int i=0;i<upperBound+1;++i) valueList[i]=0;
for(int i=1;i<=num;++i){
int tmp=(i>>1)+i/3+i/5;
valueList[tmp]++;
}
int count=0;
for(int i=0;i<upperBound+1;++i)
if(valueList[i]>0) count++;
printf("%d",count);
delete []valueList;
return 0;
}
1088. 三人行
题目分析:
有点解方程的意思,但是这里我们并不去解它,而是采用暴力穷举的方法来找到有效解。
这里需要注意,题目中的丙的能力值不一定是整数,只要满足条件就可以了,所以要用double存储。在判断方程是否成立时需要满足:
∣
P
甲
−
P
乙
∣
x
=
P
乙
y
\frac{|P_甲-P_乙|}{x}=\frac{P_乙}{y}
x∣P甲−P乙∣=yP乙
遍历甲、乙能力值时,由于他们都是整数,那么当然不能直接用上面的式子,否则会由于舍入情况不同造成即使数学上不相等程序仍判定为等于,一种解决方法是用
∣
P
甲
−
P
乙
∣
∗
y
=
P
乙
∗
x
|P_甲 -P_乙|*y=P_乙 *x
∣P甲−P乙∣∗y=P乙∗x来判断,这样都是整数运算,是可行的。我之前采用了另一种计算方法:
∣
P
甲
−
P
乙
∣
=
(
P
乙
∗
x
)
/
y
|P_甲 -P_乙|=(P_乙 *x)/y
∣P甲−P乙∣=(P乙∗x)/y,但这样答案却不对,而这里显然乘法是不会溢出的,数学上完全等价程序上也不会有截断误差,为何代码却不对呢?如果有人知道还希望指点一下。
源代码
#include <cstdio>
#include <cstdlib>
int main()
{
int myPower,x,y;
scanf("%d %d %d",&myPower,&x,&y);
int p2,p3; //表示p甲和p乙的能力值
double final[3]; //最终应取的甲、乙、丙值
final[0]=-1.0; //甲初始化为-1,为了以后寻找甲最大可行解
for(p2=99;p2>9;--p2){ //遍历寻找所有可能解
p3=(p2%10)*10+p2/10; //求出p乙
if(abs(p2-p3)*y==p3*x){ //找到满足要求的值 这里写abs(p2-p3)=(p3*x)/y却不行,why?
final[0]=p2;final[1]=p3;final[2]=final[1]/y;
printf("%d",p2); //输出甲的值
for(int i=0;i<3;++i){
if(final[i]>(double)myPower) printf(" Cong");
else if(final[i]<(double)myPower) printf(" Gai");
else printf(" Ping");
}
break;
}
}
if(final[0]<0.0)
printf("No Solution");
return 0;
}
1089. 狼人杀-简单版
题目分析:
这个简单版狼人杀,开始思路不太对,很繁琐,但是想清楚后就会很清晰。没什么很好的办法,题目给定的数据范围也不大,那就蛮力穷举吧,但穷举狼人的位置还是撒谎者的位置呢?这里显然穷举狼人的位置更好,因为题目要求我们如果出现多个解,按狼人位置序号尽量小来输出,那么如果穷举狼人位置的话从小往大试,发现一个解立即输出就好了。
源代码
#include <iostream>
#include <cstdlib>
#include <vector>
int main()
{
using namespace std;
int num;
scanf("%d",&num);
vector<int> speech(num+1);
for(int i=1;i<num+1;++i) //注意从1开始,0号不用
scanf("%d",&speech[i]);
bool find=false; //发现可行解标志
for(int wolv1=1;wolv1<num;++wolv1){
for(int wolv2=wolv1+1;wolv2<num+1;++wolv2){
vector<int> truth(num+1,1);
truth[wolv1]=truth[wolv2]=-1;
int liar=0; //统计撒谎者数目
bool wolv_liar=false; //狼人撒谎标志,一个狼人撒谎则为true,否则为false
for(int i=1;i<num+1;++i){
if(speech[i]*truth[abs(speech[i])]<0){ //表明此人撒谎了
liar++;
if(i==wolv1||i==wolv2){ //发现狼人撒谎
if(wolv_liar) wolv_liar=false;
else wolv_liar=true;
}
}
}
if(liar==2&&wolv_liar){
printf("%d %d",wolv1,wolv2);
return 0;
}
}
}
printf("No Solution");
return 0;
}