目录
7-22 人以群分 (300 分) (c)
社交网络中我们给每个人定义了一个“活跃度”,现希望根据这个指标把人群分为两大类,即外向型(outgoing,即活跃度高的)和内向型(introverted,即活跃度低的)。要求两类人群的规模尽可能接近,而他们的总活跃度差距尽可能拉开。
输入格式:
输入第一行给出一个正整数N(2≤N≤105)。随后一行给出N个正整数,分别是每个人的活跃度,其间以空格分隔。题目保证这些数字以及它们的和都不会超过231。
输出格式:
按下列格式输出:
Outgoing #: N1 Introverted #: N2 Diff = N3
其中
N1
是外向型人的个数;N2
是内向型人的个数;N3
是两群人总活跃度之差的绝对值。输入样例1:
10 23 8 10 99 46 2333 46 1 666 555
输出样例1:
Outgoing #: 5 Introverted #: 5 Diff = 3611
输入样例2:
13 110 79 218 69 3721 100 29 135 2 6 13 5188 85
输出样例2:
Outgoing #: 7 Introverted #: 6 Diff = 9359
<stdlib.h>库的qsort( )函数
作用:对传入数组进行排序
void qsort(void *base, nmemb, size, int (*compar)(const void *, const void*));
- base -- 指向要排序的数组的第一个元素的指针。
- nmemb -- 由 base 指向的数组中元素的个数。
- size -- 数组中每个元素的大小,以字节为单位。
- compar -- 用来比较两个元素的函数。
int compare (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); }
#include <stdio.h>
#include <stdlib.h>
int compare(const void *a,const void *b)
{
return (*(int*)a)-(*(int*)b);
}
int main()
{
int n,i,sum=0;
int N1,N2;
scanf("%d",&n);
int peo[n];
for(i=0;i<n;i++)
scanf("%d",&peo[i]);
qsort(peo,n,sizeof(int),compare);
for(i=0;i<n/2;i++)//俩群体的活跃度之和之差
sum+=peo[n-1-i]-peo[i];
if(n%2==0)
printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d",n/2,n-n/2,sum);
else
printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d",n/2+1,n-(n/2+1),sum+peo[n/2]);
return 0;
}
7-23 整除光棍 (300 分) (c)
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数
x
,这个整数一定是奇数并且不以5结尾。然后,经过计算,输出两个数字:第一个数字s
,表示x
乘以s
是一个光棍,第二个数字n
是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除
x
为止。但难点在于,s
可能是个非常大的数 —— 比如,程序输入31,那么就输出3584229390681和15,因为31乘以3584229390681的结果是111111111111111,一共15个1。输入格式:
输入在一行中给出一个不以5结尾的正奇数
x
(<1000)。输出格式:
在一行中输出相应的最小的
s
和n
,其间以1个空格分隔。输入样例:
31
输出样例:
3584229390681 15
#include <stdio.h>
int main()
{
int x,t=1,num=0,cnt=0;
scanf("%d",&x);
while(num<x)
{
num=num*10+1;
cnt++;
}
while(t)
{
t=num%x;
printf("%d",num/x);
if(t==0) break;//如果最后num%x==0,则说明刚好整除,不用继续了
num=t*10+1;
cnt++;
}
printf(" %d",cnt);
return 0;
}
7-24 稳赢 (300 分) (c)
现要求你编写一个稳赢不输的程序,根据对方的出招,给出对应的赢招。但是!为了不让对方输得太惨,你需要每隔K次就让一个平局。
输入格式:
输入首先在第一行给出正整数K(≤10),即平局间隔的次数。随后每行给出对方的一次出招:
ChuiZi
代表“锤子”、JianDao
代表“剪刀”、Bu
代表“布”。End
代表输入结束,这一行不要作为出招处理。输出格式:
对每一个输入的出招,按要求输出稳赢或平局的招式。每招占一行。
输入样例:
2 ChuiZi JianDao Bu JianDao Bu ChuiZi ChuiZi End
输出样例:
Bu ChuiZi Bu ChuiZi JianDao ChuiZi Bu
scanf和gets的区别
1、gets的返回值为“char*”型,scanf返回值为int型。
如果a和b都被成功读入,那么scanf的返回值就是2; 如果只有a被成功读入,返回值为1; 如果a和b都未被成功读入,返回值为0;
2、gets可以接收空格,而scanf遇到空格、回车和Tab键都会认为输入结束;
3、scanf把回车符保留在缓存中,而gets接收回车,但把回车替换为【\0】。
#include <stdio.h>
#include <string.h>
int main()
{
int k,cnt=0;
char a[10];
scanf("%d",&k);
while(scanf("%s",a))//这里用scanf 如果输入成功 返回值为1
{
if(strcmp(a,"End")==0) break;
if(cnt==k)
{
puts(a);
cnt=0;
}
else{
if(strcmp(a,"ChuiZi")==0) printf("Bu\n");
else if(strcmp(a,"JianDao")==0) printf("ChuiZi\n");
else printf("JianDao\n");
cnt++;
}
}
return 0;
}
7-25 查验身份证 (300 分) (c)
一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:
首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值
Z
;最后按照以下关系对应Z
值与校验码M
的值:Z:0 1 2 3 4 5 6 7 8 9 10 M:1 0 X 9 8 7 6 5 4 3 2
现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。
输入格式:
输入第一行给出正整数N(≤100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。
输出格式:
按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出
All passed
。输入样例1:
4 320124198808240056 12010X198901011234 110108196711301866 37070419881216001X
输出样例1:
12010X198901011234 110108196711301866 37070419881216001X
输入样例2:
2 320124198808240056 110108196711301862
输出样例2:
All passed
#include <stdio.h>
int main()
{
int n,i,j,sum,cnt=0;
char id[100][20];
int quan[17]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
char s[11]={'1','0','X','9','8','7','6','5','4','3','2'};
scanf("%d",&n);
for(i=0;i<n;i++)
{
sum=0;
scanf("%s",id[i]);
for(j=0;j<17;j++)
sum+=(id[i][j]-'0')*quan[j];
if(s[sum%11]==id[i][17]) cnt++;
else printf("%s\n",id[i]);
}
if(cnt==n)
printf("All passed");
return 0;
}
7-26 出生年 (300 分) (c)
以上是新浪微博中一奇葩贴:“我出生于1988年,直到25岁才遇到4个数字都不相同的年份。”也就是说,直到2013年才达到“4个数字都不相同”的要求。本题请你根据要求,自动填充“我出生于
y
年,直到x
岁才遇到n
个数字都不相同的年份”这句话。输入格式:
输入在一行中给出出生年份
y
和目标年份中不同数字的个数n
,其中y
在[1, 3000]之间,n
可以是2、或3、或4。注意不足4位的年份要在前面补零,例如公元1年被认为是0001年,有2个不同的数字0和1。输出格式:
根据输入,输出
x
和能达到要求的年份。数字间以1个空格分隔,行首尾不得有多余空格。年份要按4位输出。注意:所谓“n
个数字都不相同”是指不同的数字正好是n
个。如“2013”被视为满足“4位数字都不同”的条件,但不被视为满足2位或3位数字不同的条件。输入样例1:
1988 4
输出样例1:
25 2013
输入样例2:
1 2
输出样例2:
0 0001
自编半实现代码:
只能通过四位数的测试点,第二个这种个位数的测试点过不了,不如数组存储方法清晰明了
#include <stdio.h>
int main()
{
int year,y,t,n,i,cnt;
int num[10]={0};
scanf("%d %d",&year,&n);
y=year;
while(cnt!=n)
{
cnt=0;
y++;
t=y;
for(i=0;i<10;i++)
num[i]=0;
while(t)
{
num[t%10]++;
t/=10;
}
for(i=0;i<10;i++)
if(num[i]!=0) cnt++;
}
printf("%d %04d",y-year,y);
return 0;
}
#include <stdio.h>
int main()
{
int s[4];
int year,n,i,cnt=0;
scanf("%d %d",&year,&n);
for(i=year;i<3012;i++)//这个3012是根据测试点试出来的
{
cnt=1;
s[0]=i/1000;
s[1]=i/100%10;
s[2]=i/10%10;
s[3]=i%10;
if(s[0]!=s[1] && s[0]!=s[2] && s[0]!=s[3]) cnt++;
if(s[1]!=s[2] && s[1]!=s[3]) cnt++;
if(s[2]!=s[3]) cnt++;
if(cnt==n) break;
}
printf("%d %04d",i-year,i);
return 0;
}
7-27 点赞 (300 分) (c)
微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。本题就要求你写个程序,通过统计一个人点赞的纪录,分析这个人的特性。
输入格式:
输入在第一行给出一个正整数N(≤1000),是该用户点赞的博文数量。随后N行,每行给出一篇被其点赞的博文的特性描述,格式为“K F1⋯FK”,其中1≤K≤10,Fi(i=1,⋯,K)是特性标签的编号,我们将所有特性标签从1到1000编号。数字间以空格分隔。
输出格式:
统计所有被点赞的博文中最常出现的那个特性标签,在一行中输出它的编号和出现次数,数字间隔1个空格。如果有并列,则输出编号最大的那个。
输入样例:
4 3 889 233 2 5 100 3 233 2 73 4 3 73 889 2 2 233 123
输出样例:
233 3
#include <stdio.h>
int main()
{
int n,i,j,num,text;
int index[1001]={0};
int max1=0,max2=0;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&num);
for(j=0;j<num;j++)
{
scanf("%d",&text);
index[text]++;
}
}
for(i=0;i<1001;i++)
if(index[i]>=max2)//如果当前文章被点赞次数大于当前最大点赞数时
{
max1=i;//max1记录赞数最多的文章号
max2=index[i];//max2记录该文章被点赞次数
}
printf("%d %d",max1,max2);
return 0;
}
7-28 点赞狂魔 (300 分) (c++)
微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明显的特性。本题就要求你写个程序,通过统计每个人点赞的不同标签的数量,找出前3名点赞狂魔。
输入格式:
输入在第一行给出一个正整数N(≤100),是待统计的用户数。随后N行,每行列出一位用户的点赞标签。格式为“
Name
K F1⋯FK”,其中Name
是不超过8个英文小写字母的非空用户名,1≤K≤1000,Fi(i=1,⋯,K)是特性标签的编号,我们将所有特性标签从 1 到 107 编号。数字间以空格分隔。输出格式:
统计每个人点赞的不同标签的数量,找出数量最大的前3名,在一行中顺序输出他们的用户名,其间以1个空格分隔,且行末不得有多余空格。如果有并列,则输出标签出现次数平均值最小的那个,题目保证这样的用户没有并列。若不足3人,则用
-
补齐缺失,例如mike jenny -
就表示只有2人。输入样例:
5 bob 11 101 102 103 104 105 106 107 108 108 107 107 peter 8 1 2 3 4 3 2 5 1 chris 12 1 2 3 4 5 6 7 8 9 1 2 3 john 10 8 7 6 5 4 3 2 1 7 5 jack 9 6 7 8 9 10 11 12 13 14
输出样例:
jack chris john
这个跟7-27点赞 那道题很像,但是难点在于需要通过排序找出三个人,且点赞博文可能会重复,也可能存在点赞数并列的可能,在网上搜了一下这道题,发现用c++的代码量比c少很多,虽然现在没学c++,但是百度一下,基本上能读懂,set容器和sort排序老好用了。
c++中的set容器
set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列
s.begin() 返回set容器的第一个元素
s.end() 返回set容器的最后一个元素
s.clear() 删除set容器中的所有的元素
s.empty() 判断set容器是否为空
s.insert() 插入一个元素
s.erase() 删除一个元素
s.size() 返回当前set容器中的元素个数
c++中的sort()函数
void sort (first, last, cmp);
(1)first:是要排序的数组的起始地址
(2)last:是结束的地址(最后一个数据的后一个数据的地址)
(3)cmp是排序的方法:可以是从升序也可是降序。如果第三个参数不写,则默认的排序方法是从小到大排序。
#include<bits/stdc++.h>//C++的万能头文件
using namespace std;
struct dianzan{
string name;
int data; //data 实际点赞博文数,就是不算重复的
int num; //num 博文标签出现次数
}p[101];
bool cmp(dianzan a,dianzan b)
{
if(a.data!=b.data)
return a.data>b.data;//如果实际点赞博文数不相同,把实际点赞博文数按从大到小排列
else
return a.num<b.num;//如果实际点赞博文数相同,把博文标签出现次数从小到大排列
//也就是满足题目所给“输出标签出现次数平均值最小的那个”的条件
}
int main()
{
int n,K,id;//n是用户数 K是博文标签出现的次数 id是博文号
set<int> s; //定义set 容器
cin>>n;
for(int i=0;i<n;i++)
{
cin>>p[i].name;
cin>>K;
p[i].num=K;
for(int j=0;j<K;j++)
{
cin>>id;
s.insert(id); //赋值
}
p[i].data=s.size(); //因为set容器中元素都是唯一的,且默认情况下会对元素自动进行升序排列
s.clear(); //清空 set
}
sort(p,p+n,cmp); //数组的起始地址 数组结束地址 排序函数
//给这n个人的点赞情况按cmp函数中规定的规则排序
if(n<2)
{
if(n==0) cout<<"- - -";
else if(n==1) cout<<p[0].name<<" - -";
else if(n==2) cout<<p[0].name<<" "<<p[1].name<<" -";
}
else cout<<p[0].name<<" "<<p[1].name<<" "<<p[2].name;
return 0;
}
7-29 二分法求多项式单根 (300 分) (c)
二分法求函数根的原理为:如果连续函数f(x)在区间[a,b]的两个端点取值异号,即f(a)f(b)<0,则它在这个区间内至少存在1个根r,即f(r)=0。
二分法的步骤为:
- 检查区间长度,如果小于给定阈值,则停止,输出区间中点(a+b)/2;否则
- 如果f(a)f(b)<0,则计算中点的值f((a+b)/2);
- 如果f((a+b)/2)正好为0,则(a+b)/2就是要求的根;否则
- 如果f((a+b)/2)与f(a)同号,则说明根在区间[(a+b)/2,b],令a=(a+b)/2,重复循环;
- 如果f((a+b)/2)与f(b)同号,则说明根在区间[a,(a+b)/2],令b=(a+b)/2,重复循环。
本题目要求编写程序,计算给定3阶多项式f(x)=a3x3+a2x2+a1x+a0在给定区间[a,b]内的根。
输入格式:
输入在第1行中顺序给出多项式的4个系数a3、a2、a1、a0,在第2行中顺序给出区间端点a和b。题目保证多项式在给定区间内存在唯一单根。
输出格式:
在一行中输出该多项式在该区间内的根,精确到小数点后2位。
输入样例:
3 -1 -3 1 -0.5 0.5
输出样例:
0.33
#include <stdio.h>
#include <math.h>
double a0,a1,a2,a3;
double f(double x)
{
return a3*pow(x,3)+a2*pow(x,2)+a1*x+a0;
}
int main()
{
double a,b,mid;
scanf("%lf %lf %lf %lf",&a3,&a2,&a1,&a0);
scanf("%lf %lf",&a,&b);
if(fabs(f(a))<1e-5)
printf("%.2f",a);
else if(fabs(f(b))<1e-5)
printf("%.2f",b);
else if(f(a)*f(b)<0){
while(fabs(b-a)>1e-5)
{
mid=(a+b)/2;
if(f(mid)==0)
{
printf("%.2f",mid);
break;
}
else if(f(mid)*f(a)>0)
a=mid;
else if(f(mid)*f(b)>0)
b=mid;
}
}
if(fabs(b-a)<1e-5) printf("%.2f",mid);
return 0;
}
7-30 排座位 (300 分) (c)
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。
输入格式:
输入第一行给出3个正整数:
N
(≤100),即前来参宴的宾客总人数,则这些人从1到N
编号;M
为已知两两宾客之间的关系数;K
为查询的条数。随后M
行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系
,其中关系
为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K
行,每行给出一对需要查询的宾客编号。这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出
No problem
;如果他们之间并不是朋友,但也不敌对,则输出OK
;如果他们之间有敌对,然而也有共同的朋友,则输出OK but...
;如果他们之间只有敌对关系,则输出No way
。输入样例:
7 8 4 5 6 1 2 7 -1 1 3 1 3 4 1 6 7 -1 1 2 1 1 4 1 2 3 -1 3 4 5 7 2 3 7 2
输出样例:
No problem OK OK but... No way
感谢leimingze学长的指导,把jude函数里的re[b][i]==1那块,还有输出的if else补上就通过了,虽然还不是很懂为什么哈哈哈
#include <stdio.h>
#define n 10001
int re[n][n]={0};//关系数组
int N,M,K;//N是宾客人数 M是已知两两宾客之间的关系数 K为查询的条数
int judge(int a,int b)//判断a和b有没有共同好友
{
for(int i=1;i<=N;i++)
if(re[a][i]==1 && re[b][i])
return 1;
return 0;
}
int main()
{
int a,b,r,i;
int find[n][5];//把要查找的人放这里
scanf("%d %d %d",&N,&M,&K);
for(i=0;i<M;i++)
{
scanf("%d %d %d",&a,&b,&r);
re[a][b]=re[b][a]=r;//因为a人和b人间的关系是一样的
}
for(i=0;i<K;i++)//输入要查询的
scanf("%d %d",&find[i][0],&find[i][1]);
i=0;
while(K--)
{
a=find[i][0];
b=find[i][1];
if(re[a][b]==1) printf("No problem\n");
else if(re[a][b]==0) printf("OK\n");
else if(re[a][b]==-1 && judge(a,b)) printf("OK but...\n");
else if(re[a][b]==-1) printf("No way\n");
i++;
}
return 0;
}
7-31 刮刮彩票 (300 分) (c)
每次游戏玩家会拿到一张彩票,上面会有 9 个数字,分别为数字 1 到数字 9,数字各不重复,并以 3×3 的“九宫格”形式排布在彩票上。
在游戏开始时能看见一个位置上的数字,其他位置上的数字均不可见。你可以选择三个位置的数字刮开,这样玩家就能看见四个位置上的数字了。最后玩家再从 3 横、3 竖、2 斜共 8 个方向中挑选一个方向,方向上三个数字的和可根据下列表格进行兑奖,获得对应数额的金币。
数字合计 获得金币 数字合计 获得金币 6 10,000 16 72 7 36 17 180 8 720 18 119 9 360 19 36 10 80 20 306 11 252 21 1,080 12 108 22 144 13 72 23 1,800 14 54 24 3,600 15 180 现在请你写出一个模拟程序,模拟玩家的游戏过程。
输入格式:
输入第一部分给出一张合法的彩票,即用 3 行 3 列给出 0 至 9 的数字。0 表示的是这个位置上的数字初始时就能看见了,而不是彩票上的数字为 0。
第二部给出玩家刮开的三个位置,分为三行,每行按格式
x y
给出玩家刮开的位置的行号和列号(题目中定义左上角的位置为第 1 行、第 1 列。)。数据保证玩家不会重复刮开已刮开的数字。最后一部分给出玩家选择的方向,即一个整数: 1 至 3 表示选择横向的第一行、第二行、第三行,4 至 6 表示纵向的第一列、第二列、第三列,7、8分别表示左上到右下的主对角线和右上到左下的副对角线。
输出格式:
对于每一个刮开的操作,在一行中输出玩家能看到的数字。最后对于选择的方向,在一行中输出玩家获得的金币数量。
输入样例:
1 2 3 4 5 6 7 8 0 1 1 2 2 2 3 7
输出样例:
1 5 6 180
#include <stdio.h>
int main()
{
int i,j,x,y,m,sum=0,check=0;
int i0,j0;//记录那个一开始可见(即为0的数)的下标
int a[5][5],find[3][2];
int prize[25]={0,0,0,0,0,0,10000,36,720,360,80,252,108,72,54,180,72,180,119,36,306,1080,144,1800,3600};
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
{
scanf("%d",&a[i][j]);
check+=a[i][j];
if(a[i][j]==0)//如果输入的数是0,则记录它的下标
{
i0=i;
j0=j;
}
}
a[i0][j0]=45-check;//1~9之和是45,用45减去已经知道的数之和,就是那个被0代替的数,把它放在所记录的位置
for(i=0;i<3;i++)
{
scanf("%d %d",&x,&y);
find[i][0]=x;
find[i][1]=y;
}
scanf("%d",&m);
if(m<=3)
{
for(j=1;j<=3;j++)
sum+=a[m][j];
}else if(m>=4&&m<=6)
{
for(i=1;i<=3;i++)
sum+=a[i][m-3];
}else if(m==7)
{
for(i=1;i<=3;i++)
sum+=a[i][i];
}else
{
for(i=1;i<=3;i++)//求副对角线
sum+=a[i][4-i];
}
for(i=0;i<3;i++)
printf("%d\n",a[find[i][0]][find[i][1]]);
printf("%d",prize[sum]);
return 0;
}