成绩 | 10 | 开启时间 | 2021年09月13日 星期一 00:00 |
折扣 | 0.8 | 折扣时间 | 2021年09月30日 星期四 23:55 |
允许迟交 | 否 | 关闭时间 | 2021年10月17日 星期日 23:55 |
对于任意的真分数 N/M ( 0 < N < M ),均可以求出对应的小数。如果采用链表表示各个小数,对于循环节采用循环链表表示,则所有分数均可以表示为如下链表形式。
输入: N M k
输出: 转换后的小数(不超过 k )
要求: 仅编写将分数转换为小数的函数 change( int n, int m, NODE * head ) 。
预设代码
前置代码
/* PRESET CODE BEGIN - NEVER TOUCH CODE BELOW */
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{ int data;
struct node * next;
} NODE;
void output( NODE *, int );
void change( int, int, NODE * );
void output( NODE * head, int kk )
{ int k=0;
printf("0.");
while ( head->next != NULL && k<kk )
{ printf("%d", head->next->data );
head = head->next;
k ++;
}
printf("\n");
}
int main()
{ int n, m,k;
NODE * head;
scanf("%d%d%d", &n, &m, &k);
head = (NODE *)malloc( sizeof(NODE) );
head->next = NULL;
head->data = -1;
change( n, m, head );
output( head,k );
return 0;
}
/* PRESET CODE END - NEVER TOUCH CODE ABOVE */
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 2 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 3 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 4 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
代码
和上一篇一样,还是两份代码,一份学长的,一份自己的。
学长代码
#include <string.h>
void change(int n,int m,NODE *head)
{
int shang[10010],yushu[10010];
memset(shang,0,sizeof(shang));
memset(yushu,0,sizeof(yushu));
int p1=0,p2=0;
int flag=0;
int num=n*10;
for(int i=0;;i++)
{
shang[i]=num/m;
yushu[i]=num%m;
for(int j=0;j<i;j++)
{
if(shang[j]==shang[i]&&yushu[j]==yushu[i])
{
p1=j;
p2=i;
flag=1;
break;
}
}
num=yushu[i]*10;
if(!num)
{
p1=i+1;
break;
}
if(flag==1)
{
break;
}
}
NODE *r=head;
for(int i=0;i<p1;i++)
{
NODE *q=(NODE*)malloc(sizeof(NODE));
q->data=shang[i];
q->next=NULL;
r->next=q;
r=q;
}
if(flag==1)
{
NODE *r1=r;
for (int i=p1;i<p2;i++)
{
NODE *q=(NODE*)malloc(sizeof(NODE));
q->data=shang[i];
q->next=NULL;
r->next=q;
r=q;
}
r->next=r1->next;
}
}
自己代码
void change(int n,int m,NODE *head){
NODE *q=(NODE*)malloc(sizeof(NODE));
q=head;
NODE *o=(NODE*)malloc(sizeof(NODE));
o=head;
int yushu[100]={0};
int shang[100]={0};
int flag=0;
int len=0;
while(n*10/m>0){
NODE *p=(NODE*)malloc(sizeof(NODE));
p->data=n*10/m;
q->next=p;
q=q->next;
shang[len]=n*10/m;
yushu[len++]=n*10%m;
n=n*10%m;
if(n==0){
q->next=NULL;
break;
}
for(int i=0;i<len-1;i++){
if(shang[i]==shang[len-1]&&yushu[i]==yushu[len-1]){
int idx=0;
while(idx<=i){
o=o->next;
idx++;
}
int idx2=0;
NODE *r=(NODE*)malloc(sizeof(NODE));
r=head;
while(idx2<len-1){
r=r->next;
idx2++;
}
r->next=o;
flag=1;
break;
}
}
if(flag==1){
break;
}
}
}
希望学弟学妹们能帮忙测试一下我的垃圾代码,看看有什么问题,阿里嘎多。
解释
用第二份代码做题解。
核心思想
设两个数组商和余数,如果新一位的商和余数和之前某一位的商和余数相等,则该位为循环节,注意不是仅仅商相等(我改了好久才发现)。
然后类似于约瑟夫问题中循环链表的代码,把循环节连接到一块就行了。
准备工作
先搞一些指向结点的指针,这个根据个人需要弄,很多指针可以复用,我懒得想了,每次要用的时候就申请一个新指针,这使得我代码比较长,这一部分可以优化
另外准备两个记录商和余数的数组
void change(int n,int m,NODE *head){
NODE *q=(NODE*)malloc(sizeof(NODE));
q=head;
NODE *o=(NODE*)malloc(sizeof(NODE));
o=head;
int yushu[100]={0};
int shang[100]={0};
int flag=0;//为0则可以整除,为1则循环小数
做除法
就跟我们正常除法一样,有商有余数,分别保存在两个数组里,把除法的商作为新结点的值:
int len=0;
while(n*10/m>0){//当除出来的结果不为0时
NODE *p=(NODE*)malloc(sizeof(NODE));//新结点
p->data=n*10/m;
q->next=p;//一开始q是head,之后都是上一次插入的新结点
q=q->next;
shang[len]=n*10/m;//记录商
yushu[len++]=n*10%m;//记录余数
n=n*10%m; //下一次的被除数就是这一次的余数
两种情况
能整除的情况
假如可以整除,意味着最后一次除法的余数为0,也就是说最后一次的下一次被除数为0,即,只要能整除,那么总有一次n会变为0:
if(n==0){
q->next=NULL;
break;
}
不能整除的情况
在本题的条件下,不能整除的话会出现循环,我们的目的就是把循环的部分制成一个循环链表。
那么,我们只需找到循环节,然后让最后一个结点的next指针指向循环节对应的结点即可。
找到循环节需要用到我们的商和余数的数组
for(int i=0;i<len-1;i++){
if(shang[i]==shang[len-1]&&yushu[i]==yushu[len-1]){
//商和余数都相等,下一次开始循环
int idx=0;
while(idx<=i){
o=o->next;
idx++;
}//找到循环节的结点
int idx2=0;
NODE *r=(NODE*)malloc(sizeof(NODE));
r=head;//r完全可以复用之前的指针,写的时候没想
while(idx2<len-1){
r=r->next;
idx2++;
}//找到循环部分最后一个结点
r->next=o;//连接循环节和循环部分最后一个结点
//构成一个循环链表
flag=1;//不能整除的情况已经完成,可以跳出while了
break;//跳出for
}
}
if(flag==1){
break;//跳出while
}
总结
有了之前循环链表的基础,这题还是非常简单的。算法也很好想,主要就是coding了。
自己把代码敲出来还是挺爽的