A + B Again

A + B Again
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB
Total submit users: 201, Accepted users: 182
Problem 10281 : No special judgement
Problem description
Reads in positive integers A and B,calculates A + B.Pay attention:A and B is composed by simple English word. [0-9]:"zero","one","two","three","four","five","six","seven","eight","nine";
Input
Standard input will contain multiple test cases.Each testcase occupies a line.the form is "A + B =",the neighboring two characters string has a blank space.When A and B is 0 at the same time ,inputs finished.You shouldn't output the result 0.
Output
For each testcase outputs 1 line,The result of A + B,by simple English word too.Each word is separated with a single space.There's no space after the last word.
Sample Input
one + two =
three four + five six =
zero + zero =
Sample Output
three
nine zero
Problem Source
HNU Contest 
Submit   Discuss   Judge Status  Problems  Ranklist 

本人自己的解法,相当麻烦:

 
  1. #include<iostream>
  2. #include<string>
  3. #include<map>
  4. using namespace std;
  5. int pow(int x, int y)
  6. {
  7.      int sum = x;
  8.      for(int i=0; i<y; i++)
  9.      {
  10.          sum*=10;
  11.      }
  12.      return sum;
  13. }
  14. int main(int argc, char* argv[])
  15. {    
  16.     int i = 0;
  17.     int len;
  18.     int r=0;
  19.     int l=0;
  20.     char *ptr;
  21.     int t[1000];
  22.     string*s = new string[1000];
  23.     map<string,int> m;
  24.     map<int,string> m1;
  25.     m["zero"] = 0;
  26.     m["one"] = 1;
  27.     m["two"] = 2;
  28.     m["three"] = 3;
  29.     m["four"] = 4;
  30.     m["five"] = 5;
  31.     m["six"] = 6;
  32.     m["seven"] = 7;
  33.     m["eight"] = 8;
  34.     m["nine"] = 9;
  35.     m1[0] = "zero" ;
  36.     m1[1] = "one";
  37.     m1[2] = "two";
  38.     m1[3] = "three";
  39.     m1[4] = "four";
  40.     m1[5] = "five";
  41.     m1[6] = "six";
  42.     m1[7] = "seven";
  43.     m1[8] = "eight";
  44.     m1[9] = "nine";
  45.     
  46.     char in[100000];
  47.     while(gets(in))
  48.     {
  49.        len = strlen(in)+1;
  50.        ptr = strtok(in, " ");
  51.        while(ptr)
  52.        {
  53.          if(ptr[0]!='+'&&ptr[0]!='=')
  54.          {
  55.             t[i] = m[ptr];
  56.             i++;
  57.          }
  58.          else
  59.          {
  60.             
  61.              for(int k=i-1,f=0; k>=0; k--,f++)
  62.              {
  63.                  r+=pow(t[f],k);
  64.             
  65.              }
  66.             
  67.              i=0;
  68.          }
  69.          ptr = strtok(NULL," ");
  70.        }
  71.     
  72.        if(r == 0)
  73.            return 0;
  74.        int temp = r;
  75.        int count = 0;
  76.        while(r)
  77.        {
  78.           s[count++]=m1[r%10];
  79.           
  80.           r = r/10;
  81.           l++;
  82.         
  83.        } 
  84.        for(int p=count-1; p>=0; p--)
  85.        {
  86.            cout<<s[p];
  87.            if(p>0)
  88.               cout<<" ";
  89.        }
  90.        cout<<endl;
  91.         
  92.      
  93.        l=0;
  94.        r=0;
  95.       
  96.     }
  97.     
  98.     return 0;
  99. }
  100. /*在其中学会了如何使用一个函数:
  101. 原型:extern char *strtok(char *s, char *delim);
  102.         
  103.   用法:#include <string.h>
  104.   
  105.   功能:分解字符串为一组标记串。s为要分解的字符串,delim为分隔符字符串。
  106.   
  107.   说明:首次调用时,s必须指向要分解的字符串,随后调用要把s设成NULL。
  108.         strtok在s中查找包含在delim中的字符并用NULL('/0')来替换,直到找遍整个字符串。
  109.         返回指向下一个标记串。当没有标记串时则返回空字符NULL。
  110.   
  111.   举例:
  112.       // strtok.c
  113.       
  114.       #include <syslib.h>
  115.       #include <string.h>
  116.       #include <stdio.h>
  117.       main()
  118.       {
  119.         char *s="Golden Global View";
  120.         char *d=" ";
  121.         char *p;
  122.         
  123.         clrscr();
  124.         
  125.         p=strtok(s,d);
  126.         while(p)
  127.         {
  128.           printf("%s/n",s);
  129.           strtok(NULL,d);
  130.         }
  131.         getchar();
  132.         return 0;
  133.       }*/
<think>好的,我现在需要将用户提供的C语言函数`fib`翻译成RISC-V汇编代码。首先,我得仔细理解这段C代码的功能和结构。 首先,这段代码是计算斐波那契数列的第n项。让我先分析C代码的逻辑: 函数`fib`接收一个整数n,然后初始化三个变量a=0,b=1,c=a+b=1。接着进入一个while循环,条件是n>1。在循环内部,每次更新c为a+b,然后将a更新为原来的b,b更新为新的c,同时n递减。循环结束后返回c的值。 需要注意的是,当n=0或1时的情况。比如,如果n=0,根据代码,循环不会执行,直接返回c的初始值1?这里可能有问题,因为斐波那契数列通常定义为F(0)=0,F(1)=1。但原代码中,当n=1时,循环条件n>1不满足,直接返回c=1,这对应F(1)=1。当n=0的话,原代码可能有问题,因为初始时c是1,而F(0)=0。不过用户提供的代码可能意图是让n从1开始计算?或者可能有其他处理。这点需要留意,可能需要根据具体情况调整,但用户要求的是直接翻译,所以先保持原逻辑。 接下来,考虑如何将其转换为RISC-V汇编。首先,RISC-V的函数调用约定是参数通过a0传递,返回值存在a0。所以函数开始时,参数n在a0寄存器中。 变量a、b、c需要存储在寄存器中。考虑使用s0、s1、s2来保存a、b、c,因为这些是保存寄存器,在函数调用时需要保存和恢复。不过如果函数不需要调用其他函数,也可以使用临时寄存器,但为了安全起见,可能还是需要保存s寄存器,并在函数开头保存ra和s寄存器到栈上。 步骤分解: 1. 函数入口:保存必要的寄存器(s0-s2,以及ra,如果需要的话)。不过在这个函数里,可能不需要调用其他函数,所以ra不需要保存,但s寄存器如果在被调用后需要保留,则必须保存。所以函数开始时需要将s0-s2入栈,并在返回前恢复。 或者,如果使用临时寄存器t0-t6,可能不需要保存,但可能被其他函数破坏。但在这个简单函数中,可能不需要调用其他函数,所以可以安全使用t寄存器。 但根据RISC-V的规范,临时寄存器t0-t6可以在函数中被使用而不保存,保存寄存器s0-s11需要在函数中使用时先保存到栈中,并在退出前恢复。所以,为了不破坏调用者的s寄存器,如果使用s寄存器,必须保存它们。 但在这个函数中,可能变量a、b、c需要跨循环保持值,所以可能需要使用s寄存器。比如,将a存在s0,b在s1,c在s2。或者使用临时寄存器,但循环中需要多次修改这些值,而临时寄存器可能在函数调用时被修改,但在这个函数中没有函数调用,所以可以安全使用t寄存器。 这可能更高效,因为不需要保存和恢复s寄存器。因此,可以尝试使用t0、t1、t2作为a、b、c。 接下来,初始化的部分: 在C代码中,a=0,b=1,c=a+b=1。所以,在RISC-V中: li t0, 0 # a = 0 li t1, 1 # b = 1 add t2, t0, t1 # c = a + b = 1 然后,循环条件是n >1,即a0 >1。所以需要比较a0是否大于1。循环开始前,判断是否进入循环。比如: addi t3, a0, -1 # t3 = n-1,如果t3 <=0,则n<=1,不进入循环。或者使用blt指令。 所以循环的条件是:while (n >1),即当n>1时继续循环。所以,在进入循环之前,需要检查n是否大于1。如果n<=1,则跳过循环,直接返回c的值。否则,进入循环。 但原C代码中,循环的条件是while(n>1),每次循环体内n--。例如,当n=2时,循环执行一次,n变为1,退出循环,返回c。此时c是a+b的值,即原来的a=0,b=1,c=1。然后循环体执行后,a变为1,b变为1,n减到1,退出循环,返回c=1。此时对应F(2)=1?这似乎有问题,因为F(2)=1,但通常F(0)=0, F(1)=1, F(2)=1, F(3)=2,所以可能原代码在n=2时返回1,正确。但原代码的初始c=1,可能在n=1时返回c=1,当n=0时返回c=1,这可能不符合斐波那契数列的定义。但用户要求翻译,所以保持原逻辑。 因此,在RISC-V中,我们需要: 首先,将参数n从a0取出,处理初始情况。比如,如果n是0或1,返回c的初始值1。否则,进入循环。 但原代码中,当n=1时,循环不执行,返回c=1。当n=0时,同样循环不执行,返回c=1。这可能与斐波那契数列的常规定义不符,但这是原代码的逻辑,需要忠实翻译。 现在,考虑将C代码转换为汇编: 函数入口: fib: # 保存寄存器如果需要的话,假设这里使用t0-t2作为a,b,c,不需要保存 # 初始化a=0, b=1, c=1 li t0, 0 # a = 0 li t1, 1 # b = 1 add t2, t0, t1 # c = 0 +1 =1 # 处理n<=1的情况,直接返回c=1 # 原代码中,当n <=1时,返回c的值,即1 # 但原代码中的参数n是int类型,当n<=1时返回c=1 # 所以在进入循环前,判断a0是否>1,否则跳转到结束 addi t3, a0, -1 # t3 = n-1 blez t3, end # if n <=1,跳转到end loop: # 循环体开始 add t2, t0, t1 # c = a + b mv t0, t1 # a = b mv t1, t2 # b = c addi a0, a0, -1 # n-- # 检查n是否>1 addi t3, a0, -1 # t3 = new n -1 bgtz t3, loop # 如果n>1,继续循环 end: mv a0, t2 # 返回值c ret # 返回 但是这里可能有问题。因为原代码中的循环条件是while(n>1),所以在每次循环时,先执行循环体,然后n--,再判断n>1? 或者,原C代码的循环结构是: while (n > 1) { c = a + b; a = b; b = c; n--; } 所以,循环条件是在每次循环开始前检查n是否>1。因此,当n=2时,第一次进入循环,执行循环体,n减到1,然后检查n>1是否成立(此时n=1,不成立),退出循环。所以,循环体执行了一次,正确计算出F(2)=1。 在RISC-V代码中,循环的检查应该在循环的开始。例如: 在进入循环之前,先检查n>1。如果成立,执行循环体,然后n减1,再检查n>1,依此类推。 不过,原循环的结构是先检查条件,然后执行循环体。因此,在翻译时,应先检查n>1,如果成立,执行循环体,否则退出。 所以正确的流程应该是: 初始化a,b,c之后,进入循环的条件判断: loop_start: addi t3, a0, -1 # t3 = n -1 blez t3, end_loop # if n <=1,跳出循环 # 执行循环体 add t2, t0, t1 # c = a + b mv t0, t1 # a = b mv t1, t2 # b = c addi a0, a0, -1 # n-- j loop_start # 跳转回循环条件判断 end_loop: # 此时返回c的值 这样,每次循环开始前检查n>1。例如,当n=2时,第一次检查n>1成立,执行循环体,n减到1,再次跳转到循环开始,此时n=1,检查不成立,退出循环,返回c=1。 但原C代码中的循环条件是while(n>1),所以每次循环体执行后n--,然后在下一次循环开始前检查n>1是否成立。因此,正确的翻译应该是: 初始化后,检查n是否>1,如果是,进入循环体,否则结束。 这应该用以下结构: # 初始化代码 li t0,0 li t1,1 add t2, t0, t1 # 检查循环条件 li t3, 1 ble a0, t3, end # 如果n <=1,跳转到end loop: add t2, t0, t1 mv t0, t1 mv t1, t2 addi a0, a0, -1 # 检查条件 li t3, 1 bgt a0, t3, loop # 如果n >1,继续循环 end: mv a0, t2 ret 但RISC-V的ble和bgt指令可能不存在,需要用比较指令。例如,使用blt(branch if less than)来实现。 在RISC-V中,比较指令如blt(如果rs1 < rs2则跳转)。所以,判断n >1相当于判断a0 >1,即当a0 >1时继续循环。 因此,在循环开始前,需要判断是否进入循环。例如: # 初始化之后 addi t3, a0, -1 # t3 = n -1 blez t3, end # 如果n <=1,跳到end loop: # 执行循环体 add t2, t0, t1 mv t0, t1 mv t1, t2 addi a0, a0, -1 # n减1 # 检查n是否>1 addi t3, a0, -1 # t3 = n-1 bgtz t3, loop # 如果n-1 >0,即n>1,继续循环 end: mv a0, t2 ret 这样,在初始化之后,检查n是否大于1。如果是,进入循环。每次循环结束后,n减1,然后检查是否还大于1,如果还大于1,继续循环。 这可能更准确,因为: 例如,当n=2时,初始检查n-1=1>0,进入循环。执行循环体,n变为1,然后再次检查n-1=0,不满足条件,退出循环。 这样循环执行一次,正确。 现在,考虑寄存器的使用: 参数n在a0中,函数返回时结果也放在a0中。所以在初始化后,我们需要使用t0、t1、t2来保存a、b、c。 在循环体中,每次更新c为a + b,然后a变为原来的b,b变为新的c,同时n减1。 现在,编写具体的RISC-V指令: fib: # 初始化a=0, b=1, c=1 li t0, 0 # t0 = a = 0 li t1, 1 # t1 = b = 1 add t2, t0, t1 # t2 = c = 1 # 检查n是否<=1,如果是,直接返回c=1 addi t3, a0, -1 # t3 = n -1 blez t3, end # if n <=1, jump to end loop: add t2, t0, t1 # c = a + b mv t0, t1 # a = b mv t1, t2 # b = c addi a0, a0, -1 # n-- # 检查n是否>1 addi t3, a0, -1 # t3 = n -1 bgtz t3, loop # if n >1, loop again end: mv a0, t2 # return c ret # return 这样是否正确? 测试几种情况: 1. n=0:进入函数,初始化c=1,检查n-1= -1 <=0,跳转到end,返回1。但根据常规斐波那契数列,F(0)=0,但原代码返回1,可能原代码逻辑错误,但翻译时需保持原意。 2. n=1:同样返回c=1,正确对应原代码。 3. n=2:进入循环一次,c=1,返回1。但F(2)=1,正确。 4. n=3:循环执行两次? 当n=3时,初始检查n-1=2>0,进入循环体: 第一次循环: c =0 +1=1,a=1,b=1,n=2。 然后检查n=2-1=1>0,继续循环。 第二次循环: c=1+1=2,a=1,b=2,n=1。 检查n=1-1=0,不满足,退出循环,返回2。这对应F(3)=2,正确。 看来这样处理是正确的。 现在,需要注意的是,RISC-V的blez和bgtz指令是否可用。例如,blez t3, end表示如果t3 <=0则跳转。是的,在RISC-V中,blez是伪指令,等于bge x0, t3, end。同样,bgtz t3, loop等同于 blt x0, t3, loop,即如果t3 >0则跳转。 所以,上述代码是正确的。 另外,是否需要在函数开始时保存s寄存器?因为t0-t2是临时寄存器,如果这个函数被其他函数调用,并且调用者使用了这些寄存器,那么它们的值可能被破坏。但是,根据RISC-V的规范,临时寄存器不需要被被调用者保存,因此如果函数fib会破坏t0-t2的值,调用者需要负责保存它们。但在这个函数中,fib不会调用其他函数,所以可以安全使用临时寄存器,无需保存。 因此,最终的RISC-V代码应该如上述所示。</think>以下是C代码的RISC-V翻译及分步说明: ```asm fib: # 初始化a=0, b=1, c=1 li t0, 0 # t0 = a = 0 li t1, 1 # t1 = b = 1 add t2, t0, t1 # t2 = c = a + b = 1 # 边界条件判断(n ≤ 1时直接返回c=1) addi t3, a0, -1 # t3 = n-1 blez t3, end # if n ≤ 1 → 跳转到end loop: # 计算新c值 add t2, t0, t1 # c = a + b # 更新a和b mv t0, t1 # a = 旧b值 mv t1, t2 # b = 新c值 # 递减n addi a0, a0, -1 # n-- # 检查循环条件 addi t3, a0, -1 # t3 = 新n-1 bgtz t3, loop # if n > 1 → 继续循环 end: # 返回值设置 mv a0, t2 # 将c存入返回寄存器a0 ret # 返回 ``` ### 分步解析 1. **寄存器初始化** - `t0` 存储变量 $a$(初始值0) - `t1` 存储变量 $b$(初始值1) - `t2` 存储变量 $c$(初始值 $a+b=1$) 2. **边界条件处理** - 通过 `addi t3, a0, -1` 计算 $n-1$ - 若 $n ≤ 1$(即 $n-1 ≤ 0$),直接跳转到结束标签 `end` 3. **循环主体** - **计算新c值**:通过 `add t2, t0, t1` 实现 $c = a + b$ - **更新变量**: - `mv t0, t1` 将 $a$ 更新为旧 $b$ 值 - `mv t1, t2` 将 $b$ 更新为新 $c$ 值 - **递减n**:`addi a0, a0, -1` - **条件检查**:通过 `addi t3, a0, -1` 和 `bgtz t3, loop` 判断是否继续循环 4. **返回值** - 最终结果 $c$ 通过 `mv a0, t2` 存入返回寄存器 ### 关键特性 1. **寄存器选择**:使用临时寄存器 `t0-t2` 避免保存/恢复开销 2. **循环优化**:通过递减后直接判断 $n > 1$ 减少指令数 3. **边界处理**:通过预计算 $n-1$ 简化条件判断 ### 示例行为验证 - **输入 $n=3$**: 1. 初始 $a=0, b=1, c=1$ 2. 第一次循环:$c=1$, $a=1$, $b=1$, $n=2$ 3. 第二次循环:$c=2$, $a=1$, $b=2$, $n=1$ 4. 返回 $c=2$(符合 $F(3)=2$)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值