32
程程程程程程 3 程程程程程程 2012-3

程序设计实习 3 月份练习 解答

Embed Size (px)

DESCRIPTION

程序设计实习 3 月份练习 解答. 2012-3. POJ普遍问题. Wrong Answer 错误的算法 理解错误 Time Limit Exceeded 程序运行超时 优化程序 改变算法. POJ普遍问题2. Output Too Much 程序输出太多 看看你的代码是否有按照题意的要求输入输出和结束 Runtime Error 除0 内存非法使用(数组越界,使用指向非法地址的指针). A: 计算对数. 描述 : 给定两个正整数 a 和 b 。可以知道一定存在整数 x ,使得 x

Citation preview

Page 1: 程序设计实习 3 月份练习 解答

程序设计实习 3 月份练习解答

2012-3

Page 2: 程序设计实习 3 月份练习 解答

POJ 普遍问题• Wrong Answer – 错误的算法– 理解错误

• Time Limit Exceeded– 程序运行超时• 优化程序• 改变算法

Page 3: 程序设计实习 3 月份练习 解答

POJ 普遍问题 2

• Output Too Much– 程序输出太多– 看看你的代码是否有按照题意的要求输入输出

和结束• Runtime Error– 除 0– 内存非法使用 ( 数组越界,使用指向非法地址

的指针 )

Page 4: 程序设计实习 3 月份练习 解答

A: 计算对数• 描述 : 给定两个正整数 a 和 b 。可以知道一定存在

整数 x ,使得 x <= logab < x + 1输出 x

• 输入 : 第 1 行是测试数据的组数 n ,每组测试数据占 2 行,分别是 a 和 b 。每组测试数据之间有一个空行,每行数据不超过 100 个字符

• 输出 :n 行,每组测试数据有一行输出,也就是对应的 x 。输入数据保证 x 不大于 20

Page 5: 程序设计实习 3 月份练习 解答

A: 计算对数• 描述 : 给定两个正整数 a 和 b 。可以知道一定存在

整数 x ,使得 x <= logab < x + 1输出 x

• 输入 : 第 1 行是测试数据的组数 n ,每组测试数据占 2 行,分别是 a 和 b 。每组测试数据之间有一个空行,每行数据不超过 100 个字符

• 输出 :n 行,每组测试数据有一行输出,也就是对应的 x 。输入数据保证 x 不大于 20

Page 6: 程序设计实习 3 月份练习 解答
Page 7: 程序设计实习 3 月份练习 解答

同学们的算法1. 大整数乘法:

对 i ,依次用 a^i 与 b 比较2. 取大整数前 6 位 , 用 log 函数 , 转换为 logb / loga

3. 直接用 log 函数,转换为 logb / loga 这样可以吗?

• float 占用 4 字节空间,大致取值范围是 (±)10 的 -38 次方到 10 的 38次方,精度是 6 位有效数字

• double 占用 8 字节空间,大致取值范围是 (±) 10 的 -308 次方到 10 的308 次方,精度是 15 位有效数字。

Page 8: 程序设计实习 3 月份练习 解答

• IEEE 754 标准

• N=(-1)sx(1+M)x2E- 偏移

• float : N=(-1)sx(1+M)x2E-127 ( 0<E<255 )• double : N=(-1)sx(1+M)x2E-1023 ( 0<E<2046 )

M:尾数( 0~1之间)E:指数

计算对数

Page 9: 程序设计实习 3 月份练习 解答

#include<iostream> #include<cmath> using namespace std;int main(){

int n;double a,b;cin>>n;while(n--){

cin>>a>>b;cout<<int(log10(b)/log10(a))<<endl;

}}

计算对数

Page 10: 程序设计实习 3 月份练习 解答

B: 宇航员• 描述:宇航员,在他的起始位置现在建立一个虚拟 xyz 坐标系,称

为绝对坐标系,宇航员正面的方向为 x 轴正方向,头顶方向为 z 轴正方向,则宇航员的初始状态如下图所示:

现对六个方向分别标号, x , y , z 正方向分别为 0 , 1 , 2 ,负方向分别为 3 , 4 , 5 ;

Page 11: 程序设计实习 3 月份练习 解答

宇航员• 任务描述:

  请根据宇航员对自己在相对方向上移动的描述确定宇航员最终的绝对坐标和面向的绝对方向。对在相对方向上移动的描述及意义如下:forward x  向前走 x 米。back x  先转向后,再走 x 米。left x 先转向左,再走 x 米。right x 先转向右,再走 x 米。up x 先面向上,再走 x 米。down x 先面向下,再走 x 米。

Page 12: 程序设计实习 3 月份练习 解答

宇航员

Page 13: 程序设计实习 3 月份练习 解答

#include<iostream>

using namespace std;

int x[6], p, head;

int turnL[6][6] = {{0,5,1,0,2,4},{2,0,3,5,0,0}, {4,0,0,1,3,0}, {0,2,4,0,5,1},{5,0,0,2,0,3},{1,3,0,4,0,0}};

int turnR[6][6] = {{0,2,4,0,5,1},{5,0,0,2,0,3}, {1,3,0,4,0,0}, {0,5,1,0,2,4},{2,0,3,5,0,0},{4,0,0,1,3,0}};

宇航员

Page 14: 程序设计实习 3 月份练习 解答

void CalPositon()

{

char str[20];

int num,temp;

cin>>str;

cin>>num;

switch(str[0])

{

case 'f':

break;

case 'b':

p=(p+3)%6;

break;

case 'l':

p=turnL[head][p];

break;

case 'r':p=turnR[head]

[p];break;

case 'u':temp = p;p=head;head =

(temp+3)%6;break;

case 'd':temp = p;p=(head+3)%6;head = temp;break;

} x[p] += num;}

宇航员

Page 15: 程序设计实习 3 月份练习 解答

int main()

{

int t;

cin>>t;

int i;

for(i=0; i<t;i++)

{

int z = 5;

while(z>=0)

{

x[z] = 0;z--;

}

p=0;

head=2;

int k;

cin>>k;

int j;

for(j=0; j<k; j++) CalPositon();

cout<<x[0]-x[3]<<' '<<x[1]-x[4]<<' '<<x[2]-x[5]<<' '<<p<<endl;

}

return 0;

}

宇航员

Page 16: 程序设计实习 3 月份练习 解答

另一种模拟方法:• 用 p , head , left (或者 right )三个方向标识当前方向– Left : head 不变, p 变为 left , left 变为 p 的反方向

作业中的问题:– 有些同学做的过于繁琐,大片大片的 if else , switch

– 对于这类模拟题的建议:• 找规律,把各类情况综合起来• 用好数组: turn[6][6][6] turn[head][p][1-6]

宇航员

Page 17: 程序设计实习 3 月份练习 解答

C :特殊日历计算• 有一种特殊的日历法,它的一天和我们现在用的

日历法的一天是一样长的。• 思路:– 传统日历距离 0:0:0 1.1.2000 的天数

• = 新日历天数– 10 天算一周, 10 周算一个月, 10 个月算一年– 24*60*60a = 10*100*100b

• 换算 == 》 新日历秒数– 每天有有 10 个小时,每个小时有 100 分钟,每分钟有

100 秒• bool IsLeapYear(int year)– (year % 4 == 0 && year % 100 !=0 ) || (year % 400 == 0)

Page 18: 程序设计实习 3 月份练习 解答

D :循环数• 142857 *1 = 142857

142857 *2 = 285714 142857 *3 = 428571 142857 *4 = 571428 142857 *5 = 714285 142857 *6 = 857142

• 大整数乘法• 判断结果是否是循环数

Page 19: 程序设计实习 3 月份练习 解答

F :浮点数加法• 题目中输入输出中出现浮点数都有如下的形式:P1P2...Pi.Q1Q2...Qj

对于整数部分, P1P2...Pi 是一个非负整数对于小数部分, Qj 不等于 0

• 求 2 个浮点数相加的和– 小数点对齐–修改版的大整数相加• 略过小数点

Page 20: 程序设计实习 3 月份练习 解答

F: 数根 思路• 需要注意什么?• 10^1000 -> char[1002]• 在累加的过程中,始终保持结果为个位数• 比如:– 987– 9 + 8 + 7 = 24 6

– 987– 9 + 8 = 17 8– 8 + 7 = 15 6

Page 21: 程序设计实习 3 月份练习 解答

F: 数根 实现• 示例代码

char c;int result = 0;while (cin>>c && c!=‘\n’) {

result += (c-’0’);result = result/10 + result%10;

}cout << result << endl;

Page 22: 程序设计实习 3 月份练习 解答

G: 武林 大局• 需要哪些类?–武士(一个基类,三个子类)–世界

Page 23: 程序设计实习 3 月份练习 解答

G: 武林 分析• 各门派弟子共性:–属性:内力、武艺、和生命力,攻击力,所处

的格子– 行为:运动、攻击– 状态:是否死亡

• 各门派弟子特性:– 运动方式不同–攻击力计算方式不同

Page 24: 程序设计实习 3 月份练习 解答

G: 武林 设计class Warrior {protected:

int force; // 内力int skill; // 武艺int life; // 生命

int row;int col;

public:Warrior();virtual int getDamage();virtual void moveOneStep();bool isDead();

};

Page 25: 程序设计实习 3 月份练习 解答

G: 武林• 世界:

– 属性:大小、弟子– 行为:往里添加一个弟子、运行、输出当前状态

class World {private:

int row;int col;Warrior* warriors[1000];

public:World();void run(); // 开始运行void addWarrior(Warrior*);void printStatus();

};

Page 26: 程序设计实习 3 月份练习 解答

G: 武林 main 函数• main 函数干什么事情?–生成一个 World 实例–获取输入,传递给 World生成Warrior–全部输入完成后,启动 World.run()开始运行

n步– 输出结果

Page 27: 程序设计实习 3 月份练习 解答

G: 武林 弟子如何运动• 以少林弟子为例,少林弟子在同一列不停运动• 定义两个方向– rowDirection = 1– colDirection = 0

• 运动时– newRow = row + rowDirection– newCol = col + colDirection

• 若无法在原方向运动,则反向– rowDirection = - rowDirection– colDirection = - colDirection

Page 28: 程序设计实习 3 月份练习 解答

G: 武林 判断是否发生战斗• 当有两名不同门派的弟子进入同一个格子

时,不会自相残杀;一个格子里三派 弟子都有时一定会发生一次战斗,而且也只有在这种情况下,才会发生战斗。(同派弟子之间当然,大家都会因为害怕别人渔翁得利而不敢出手;而多名同门派弟子也不会联手对付敌人,因为这有悖于武林中崇尚的单打独斗精神,会被人耻笑)

Page 29: 程序设计实习 3 月份练习 解答

G: 武林 判断是否发生战斗• 格子里有几个弟子?– 用数组计数– 在新增弟子和弟子运动时,维护格子的计数– 扫描全部格子,确定哪些格子有两个弟子

• 格子里有哪些弟子?– 方法 1 :对格子 (i, j) ,扫描所有弟子确定哪两个弟子在 (i, j) 上

– 方法 2 :定义一个格子类,类内用指针指向当前在格子里的弟子

– 其他方法

Page 30: 程序设计实习 3 月份练习 解答

H: 词典 思路• 难点–词典中包含不超过 100000 个词条–词条长度不超过 10– 不超过 100000查询单词– 如果逐个查找,最坏情况就要做 100000*

100000*10 计算– 根据经验 3000ms 内, poj服务器最多可做

10^9 次运算• 解决办法:–二分查找– 100000*log ( 100000 ) *10

Page 31: 程序设计实习 3 月份练习 解答

H: 词典 实现样例 1• const int MAXLEN=12;• const int MAXNUM=100010;• struct dicEntry{• char a[MAXLEN],b[MAXLEN];• }dic[MAXNUM];• int cmp(const void *x,const void *y)• {• return strcmp(((dicEntry*)x)->b,((dicEntry*)y)->b);• }• // 二分查找• dicEntry* myBinSearch(dicEntry* x,dicEntry* dic,int n)• {• int left=0,right=n-1,mid,con;• while (left<=right){• mid=(left+right)>>1;• if ((con=cmp(x,dic+mid))<0) // 比中间的字符串小,那么范围就缩小到左半边• right=mid-1;• else if (con>0) // 比中间的字符串大,那么范围就缩小到右半边• left=mid+1;• else return dic+mid;• }• // 没有找到• return NULL;• }

Page 32: 程序设计实习 3 月份练习 解答

H: 词典 实现样例 2• int main()• {• int n=0;• while(scanf("%[a-z]%*c",dic[n].a)){• scanf("%[a-z]%*c",dic[n].b);• n++;• }• qsort(dic,n,sizeof(dicEntry),cmp); //排序• getchar();• dicEntry tmp;• while (gets(tmp.b)){• // dicEntry *p=(dicEntry *)bsearch(&tmp,dic,n,sizeof(dicEntry),cmp);• // bsearch 是 STL里的,直接调用也可以,实际也是二分查找• dicEntry *p=myBinSearch(&tmp,dic,n); //二分代码示例• if (p!=NULL) cout <<p->a <<endl;• else cout <<"eh\n";• }• }