2.1 例题:鸡兔同笼
【目测gcc编译环境下C和C++略不同,上篇提到的Structure问题,文件保存为cpp后缀即可调试通过。】
#include "stdio.h"
int main(){
int a;
int min,max;
int number;
scanf("%d",&number);
int i,array[number];
for(i=0;i<number;i++){
scanf("%d",&array[i]);
}
for(i=0;i<number;i++){
a=array[i];
if(a%4==0){
min=a/4;
max=a/2;
}
else if(a%2==0){
min=1+(a-2)/4;
max=a/2;
}
else{
min=max=0;
}
printf("%d %d\n",min,max);
}
}
官方代码
#include "stdio.h"
int main(){
int nCases,i,nFeet;
scanf("%d",&nCases);
for(i=0;i<nCases;i++){
scanf("%d",&nFeet);
if(nFeet%2!=0){
printf("0 0\n");
}
else if(nFeet%4!=0){
printf("%d %d\n",nFeet/4+1,nFeet/2);
}
else
printf("%d %d\n",nFeet/4,nFeet/2);
}
return 0;
}
质疑:这样会输入一个值,马上输出结果,并不满足输出样例的要求啊
老大说,只要输出的结果对就行
2.2 例题:棋盘上的距离
额,被gcc给坑了,没能尽快做完题,gcc下不能简单调用math.h头文件
http://blog.sina.com.cn/s/blog_53ed87c1010002p7.html
程序中用到数学函数.原本只要在程序中加上面这一行就行了.
gcc不知道犯什么毛病.不仅程序里要有include,编译的时候也要在指令后面加-lm
否则就这德行了:
/tmp/cc4FFwdt.o(.text+0x126): In function `main':
: undefined reference to `sqrt'
/tmp/cc4FFwdt.o(.text+0x169): In function `main':
: undefined reference to `sqrt'
collect2: ld returned 1 exit status
编译时像这样就行
gcc -o abc abc.c -lm
-*-*-*-*-*-*-
参考:
Q. 编译程序时得到undefined reference to 'xxxx'这样的错误提示
A: 那你一定是缺少某个库,用 -l参数将库加入。Linux的库命名是一致的,一般为libxxx.so,或libxxx.a,libxxx.la,那么你要链接某个库就用-lxxx,去掉头lib及"."后面的so,la,a等即可。
同时,常见的库链接方法为:
数学库 -lm ; posix线程 -lpthread
总之,集成gcc到Editplus没法用参数(也不会用),math.h算是废了。。。需要的时候拷贝到VC中运行吧
好吧,开始做题,本题要求对输入的每组测试数据,输出王、后、车、象所需多少步数,若无法到达,输出“Inf”。
题目看完真没思路,看了看解析,编写代码如下
//2.2 棋盘上的距离
#include "stdio.h"
#include "math.h"
int min(int x,int y){
if(x<y)
return x;
else
return y;
}
int main(){
int nCase;
int x,y;
char begin[5],end[5];//分别存储起始位置;
char wang,hou,che,xiang;//存储王、后、车、象的最短步数
scanf("%d",&nCase);
for(int i=0;i<nCase;i++){
scanf("%s %s",&begin,&end);
x=abs(begin[0]-end[0]);
y=abs(begin[1]-end[1]);
if(x==0&&y==0){
printf("0 0 0 0\n");
continue;
}
//分别计算四种棋子
wang=abs(x-y)+min(x,y);
if(x==y||x==0||y==0){
hou=1;
}else
hou=2;
if(x==0||y==0){
che=1;
}else
che=2;
if(x==y){
xiang=1;
}else if((x+y)%2==1){
xiang=-1;
}else
xiang=2;
printf("%d %d %d %d\n",wang,hou,che,xiang);
}
return 0;
}
Summary
1、算法题中不必想将所有变量存储起来,最后一起输出(如42行),只要满足题目要求的输出的条件,不用定义变量,多个printf也无所谓,本代码将所有变量存起来导致无法输出Inf,只能以-1代替之;
2、输入为char的a,b等字符,直接相减及时起止位置的水平距离;(考虑字符串取出第一个,字符串转字符串数组...方向不对)
3、要考虑起止位置的x方向y方向绝对距离,据此判断步数,不应考虑8X8矩阵64个位置绝对坐标之间的数学关系;
4、深感题目之严谨,个人之不足,以1为例,输出不了Inf必然0分;
5、我居然定义了C中string类型变量,还找不出错!
6、根据王的行走规则,所需要的步数是min(x,y)+abs(x-y);
刚好为max(x,y);
注意: C中没有string类型变量,只有字符串数组
官方代码
#include "stdio.h"
#include "stdio.h"
int main(){
int nCase;
int x,y;
char begin[5],end[5];//分别存储起始位置;
scanf("%d",&nCase);
for(int i=0;i<nCase;i++){
scanf("%s %s",begin,end);
x=abs(begin[0]-end[0]);
y=abs(begin[1]-end[1]);
if(x==0&&y==0){
printf("0 0 0 0\n");
}else{
//分别计算四种棋子
if(x>y) printf("%d",x);//wang
else printf("%d",y);
if(x==y||x==0||y==0){//hou
printf(" 1");
}else printf(" 2");
if(x==0||y==0){//che
printf(" 1");
}else printf(" 2");
if(x==y){ //xiang
printf(" 1\n");
}else if((x+y)%2==1){ //条件也可为abs(x-y)%2!=0
printf(" Inf\n");
}else
printf(" 1\n");
}
}
return 0;
}
2.3 例题:校门外的树
输入一行有两个整数,L(1<=L<10000)和M(1<=M<=100),L代表马路长度,M代表施工区域,求剩下的树。
#include "stdio.h"
int main(){
int nCase;
int length;
int begin,end;//施工范围
scanf("%d%d",&length,&nCase);
int temp[length];
int count=0;
for (int i=0;i<length ;i++ )
{
temp[i]=0;
}
for (int i=0;i<nCase ;i++ ) //输入if、for之后紧跟着空格键可自动补全括号
{
scanf("%d%d",&begin,&end);
for (int i=begin;i<=end ;i++ )
{
temp[i]=-1;
}
}
for (int i=0;i<=length ;i++ )
{
if (temp[i]!=-1)
{
count++;
}
}
printf("%d\n",count);
}
Summary
1、codepad提示
Line 7: error: ISO C++ forbids variable-size array 'temp'
,但本地调试的时候可以正常通过,官方代码为避免这一点,定义了一个长度为10001的trees数组。
问了问实验室老大,答曰:定义固定长度的数组,按官方的来。
2、本章为简单计算题,我一直在想怎么用数学方法快速算出,无果,看看答案和我一样使用笨方法。。。无语
2.1 考虑过用left和right标记1到length的连续数字中被砍掉部分的边界,但如果用户输出两个不想交的子序列就坑爹了,不好办
2.2 考虑为了提高性能,用一个大数组存储每次输入的序列(用户负责输入bengin和end),问题演变为有序数字序列中插入一段有序数字序列,但如果用户输入3段互不相交的数字序列。。。。傻眼了。
2.3 最终采用最笨方法,标记数组每个位置的数值,初始都为0,砍掉后为-1,最后循环统计整个数组中不为-1的元素个数。
3、官方用true和false标记,看起来效率高些。
4、肯定有高效的数学方法,官方提示:合并小区间、数被移走的树的数目然后用length减。
官方代码略
2.4 例题:填词
原题很长,要在一个矩阵方格盘中找到指定的单词,按字典序输出剩下的字母,即所谓的神秘单词,题目要求找单词的过程有诸多限制,但问题似乎和找单词没什么关系。
若从答案对的角度出发,第一次思考考虑建立10*10的二维数组(N和M的范围限定在2到10之间),将用户输入的字母赋值到相应位置,逐个字母接收到要找出的单词之后,每次检索二维数组,删除第一个出现的元素(后来想想怎么越来越复杂了...),最后按字典序输出剩下的单词(额,按字母顺序输出又是一个额外的任务)
按此思路写代码,写不下去了→_→看解题思路:
解题思路里将问题抽象之后为:给定一个字母的集合从中去掉一些在给定单词中出现过的字母。
用结构体试试吧
先测试一下结构体基本功能预热一下
//使用结构体的新思路
#include "stdio.h"
struct game
{
char data;
int count;
};
int main(){
int i,j;
game masterWord[26];//定义结构体数组
masterWord[0].data='A';
masterWord[1].data=masterWord[0].data+1;
printf("%c\n",masterWord[1].data);
return 0;
}
成功输出:B
注意:第14行若写成masterWord[0].data="A";则报错invalid conversion from `const char*' to `char';
注意:考虑初始化结构体数组masterWord,若不初始化,printf一下看看其定义之后里面是什么:
€ 0 T 0 x 5836408
所以需要循环赋值初始化。接着,
scanf("%d%d%d",&N,&M,&P);
printf("输入参数为%d %d %d\n",N,M,P);
for (i=0;i<N*M ;i++ )//循环输入
{
printf("N*M=%d\n循环输入开始\n",N*M);
scanf("%c",&temp);
printf("temp=%c ",temp);
j=temp-'A';
printf("j=%d\n",j);
masterWord[j].data=temp;
masterWord[j].count++;
}
第一行输入 3 3 2之后最后的回车符会被for循环里面的scanf接收,马上显示temp为空,j=-55(备注:代码中间printf用于测试,最后会注释掉);
试了试在for循环第一行加一句getchar()吃掉回车符,但副作用是这回吃掉下一行输入的EBG中的B,悲剧了。
//使用结构体的新思路
#include "stdio.h"
struct game
{
char data;
int count;
};
int main(){
int i,j;
int N,M,P;
char temp;
game masterWord[26];//定义结构体数组
for (i=0;i<26;i++ )
{
masterWord[i].data='0';
//printf("初始化%d,%c\n",i,masterWord[i].data);
masterWord[i].count=0;
}
scanf("%d%d%d",&N,&M,&P);
printf("输入参数为%d %d %d\n",N,M,P);
for (i=0;i<N*M ;i++ )//循环输入
{
printf("N*M=%d\n循环输入开始\n",N*M);
scanf("%c",&temp);//temp=getchar()与scanf("%c",&temp)等效都接收回车符
printf("temp=%c ",temp);
j=temp-'A';
printf("j=%d\n",j);
masterWord[j].data=temp;
masterWord[j].count++;
}
for (i=0;i<P*M ;i++ )//循环删除
{
printf("循环删除,请输入\n");
scanf("%c",&temp);
j=temp-'A';
masterWord[j].count--;
if (masterWord[j].count==0)
{
masterWord[j].data='0';
}
}
for (i=0;i<26 ;i++ )
{
while (masterWord[i].count>0)
{
printf("%c",masterWord[i].data);
masterWord[i].count--;
}
}
printf("\n");
return 0;
}
此处搞不定了,每次都把回车符输进去
官方答案:
//使用结构体的新思路
#include "stdio.h"
int main(){
int i,j;
int n,m,p;
int word[26];//分别对应26个字母每一个的数量
for (i=0;i<26 ;i++ )//初始化数组
{
word[i]=0;
}
scanf("%d%d%d",&n,&m,&p);
char temp[10];//存放每一行的数据
for (i=0;i<n ;i++ )
{
scanf("%s",temp);//'\0' 是字符串的结束符,任何字符串之后都会自动加上'\0'
for (j=0;temp[j]!='\0' ;j++ )
{
word[temp[j]-'A']++;
}
}
for (i=0;i<p ;i++ )//读入p个单词,去掉这p个单词,官方答案定义了str[200]存储每行;
{
scanf("%s",temp);
for (j=0;temp[j]!='\0' ;j++ )
{
word[temp[j]-'A']--;
}
}
for (i=0;i<26 ;i++ )//输出所有
{
while (word[i]>0)
{
printf("%c",'A'+i);
word[i]--;
}
}
printf("\n");
return 0;
}
Summary
1、我方向又搞错了,每一行的输入可以用字符串数组来接收,不必非要一个一个接收;
2、又浪费了一晚上,关于C语言scanf函数详细解释,有空在深入看看;
2.5 例题:装箱问题
装箱问题本质上是把边长分别为1、2、3、4、5、6的小正方形填充到边长为6的大正方形中,每一类的小正方形的数量需要由用户临时给定,所以考虑随机得到各类数量的时候,需要先大后小,最大程度利用空隙填充剩下的小正方形。
小正方形单独填充的情况:
边长 | 1 | 2 | 3 | 4 | 5 | 6 |
一个大正方形最多放几个 | 36 | 9 | 4 | 1 | 1 | 1 |
接下来从大到小考虑剩下的空隙:
边长 | 最多几个 | 空隙 |
6 | 1 | 无 |
5 | 1 | 11个1*1的小正方形【以temp5暂时存储之,下同】 |
4 | 1 | 5个2*2【temp4】 |
边长为3的小正方形填充需要分情况
数量 | 空隙 |
4的倍数 | 无 |
4的倍数+1 | 5个2*2【temp321】,7个1*1【temp322】 |
4的倍数+2 | 3个2*2【temp331】,6个1*1【temp332】 |
4的倍数+3 | 1个2*2【temp341】,5个1*1【temp342】 |
边长为2的小正方形填充需要分情况
数量为9的倍数 | 无 |
数量为9的倍数+ | 剩下的都用1*1填充 |
定义box[7],其中1-6存储6个类型的数量,为节约空间,第0个存储本次数据的最少箱数。
按照表格分析的思路编写代码,题目要求循环输入,以全0作为结束标志,我们先实现一次输入6个,计算出结果,以验证前面思路:
//装箱问题
#include "stdio.h"
int main(){
int i,j;
int box[7];//box[0]用于存储每一组计算出的最少箱子
int temp5,temp4;
int temp321,temp322,temp331,temp332,temp341,temp342;
int temp;
temp=temp5=temp4=temp321=temp322=temp331=temp332=temp341=temp342=0;
scanf("%d%d%d%d%d%d",&box[1],&box[2],&box[3],&box[4],&box[5],&box[6]);
//box[0]初始化
box[0]=0;
//开始计算
if (box[6]>0)
{
box[0]=box[6];
}
if (box[5]>0)
{
box[0]=box[0]+box[5];
temp5=box[5]*11;//存放1X1的空余位置
}
if (box[4]>0)
{
box[0]=box[0]+box[4];
temp4=box[4]*5;//存放2X2的空余位置
}
if (box[3]>0)
{ //int之间除法比如3.7会变成3
temp=1+box[3]/4;//确定要开几个新箱子
box[0]=box[0]+temp;
temp=box[3]%4;//取余
switch (temp)
{
case 0: //若不加break,后面的语句也会执行
break;
case 1:
temp321=5,temp322=7;
break;
case 2:
temp331=3,temp332=6;
break;
case 3:
temp341=1,temp342=5;
break;
}
}
temp=temp4+temp321+temp331+temp341; //前面装箱剩下的2*2的空隙
int space=0;
if (box[2]>0)
{
if (box[2]<=temp)
{
temp=temp-box[2];//记录剩下的2*2的空间
}else{
temp=box[2]-temp;
space=1+temp/9;//多开几个箱子
box[0]=box[0]+space;//新箱子数量
temp=9-temp%9;//剩余的放2*2的空间
}
}
space=temp5+temp322+temp332+temp342;//前面装箱剩下的1X1的空隙
temp=temp*4+space;//全部剩下的1*1空隙
if (box[1]>0)
{
if (box[1]>temp)
{
temp=box[1]-temp;
space=temp/36;//多开几个箱子
box[0]=box[0]+space;//新箱子数量
//还剩多少空间不用算了
}
}
printf("%d\n",box[0]);
}
上面需要修改讨论box[3]和box[2]的计算,假如除法结果为0.6、1、1.2的时候,我们需要的箱子数量分别是1、1、2,不是单纯的向上或者向下取整,【再次纠结了很久,没找到什么数学方法】,只好在不同case情况下分别计算box[0]。
最少箱子数目要修改的代码:
if (box[3]>0)
{ //除法比较麻烦,要使得3/4=1,4/4=1,5/4=2;
temp=box[3]%4;//取余
switch (temp)
{
case 0: //若不加break,后面的语句也会执行
temp=box[3]/4;//整除的情况
box[0]=box[0]+temp;
break;
case 1:
temp321=5,temp322=7;
temp=1+box[3]/4;//有余数
box[0]=box[0]+temp;
break;
case 2:
temp331=3,temp332=6;
temp=1+box[3]/4;//有余数
box[0]=box[0]+temp;
break;
case 3:
temp341=1,temp342=5;
temp=1+box[3]/4;//有余数
box[0]=box[0]+temp;
break;
}
}
temp=temp4+temp321+temp331+temp341; //前面装箱剩下的2*2的空隙
int space=0;
if (box[2]>0)
{
if (box[2]<=temp)
{
temp=temp-box[2];//记录剩下的2*2的空间
}else{
temp=box[2]-temp;
if (temp==0)//整除的情况
{
space=temp/9;//多开几个箱子
box[0]=box[0]+space;//新箱子数量
}else{ //有余数
space=1+temp/9;//多开几个箱子
box[0]=box[0]+space;//新箱子数量
}
temp=9-temp%9;//剩余的放2*2的空间
}
}
以上红字部分搞错了,本题需要的是向上取整,比如(x+n-1)/n即可实现x对n的向上取整,只要修改源代码赋值那句即可
最后调试没问题,每次输出都满足要求,下一步就简单了,将以上代码封装成int box(),主函数循环调用即可:
int main(){
int box();//声明box()
int a;
int i=0;
int jason[100];
for (i=0;i<100 ;i++ )//初始化
{
jason[i]=0;
}
i=0;//坑死了,此处i归0!!!!!!
while ((a=box())>=0)
{
jason[i]=a;
i++;
}
i=0;
while(i<100&&jason[i]>0 )//输出算出的每个值
{
printf("%d\n",jason[i++]);
}
}
官方代码:
字数超过最大允许值,服务器可能会拒绝保存哟哟哟,切克闹
见下一篇:第二章 简单计算题之课后题
Summary
1、本题中官方代码非常完善,我代码中固定的数字没必要用temp***来表示,需要的时候直接用就行;
2、向上取整没想明白,3*3的情况下直接用(box[3]+3)/4即可算出需要的箱子;
3、代码逻辑多处多余,6*6到3*3的情况都不需要用到判断!!;
4、没必要考虑每次1*1剩下的情况,更没必要将剩下多少存储起来,最后直接减一下就得到了;