【POJ】第二章 简单计算题之例题

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;

}

 

 

此处搞不定了,每次都把回车符输进去

205445_h6yE_576429.png

官方答案:

//使用结构体的新思路
#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的大正方形中,每一类的小正方形的数量需要由用户临时给定,所以考虑随机得到各类数量的时候,需要先大后小,最大程度利用空隙填充剩下的小正方形。

小正方形单独填充的情况:

 

边长123456
一个大正方形最多放几个3694111

接下来从大到小考虑剩下的空隙:

 

边长最多几个空隙
61
5111个1*1的小正方形【以temp5暂时存储之,下同】
415个2*2【temp4】

边长为3的小正方形填充需要分情况

数量空隙
4的倍数
4的倍数+15个2*2【temp321】,7个1*1【temp322】
4的倍数+23个2*2【temp331】,6个1*1【temp332】
4的倍数+31个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剩下的情况,更没必要将剩下多少存储起来,最后直接减一下就得到了;

 

转载于:https://my.oschina.net/SnifferApache/blog/288476

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值