对一道题的算法分析

这个问题是这样的:

时限:1000ms 内存限制:10000K  总时限:3000ms

在一个监狱里面有n个锁着的相邻的牢房。一天晚上狱卒
打算玩个游戏。在游戏的第一回合,他会打开每个牢房的锁。
第二个回合,他会锁上第2,4,6,8,。。。这些牢房。第三回合
他会走到所有号码是3倍数的牢房,例如 3,6,9等等,如果那些牢房是锁着的,他就打开锁,如果是开着的,他会锁上。接下的所有回合都类似这样,比如第m个回合,他会走到所有号码是m倍数的牢房。如果那些牢房是锁着的,他就打开锁,如果是开着的,他会锁上
游戏会进行进行n个回合。请问最后有多少个牢房是开着的?

输入:

正整数n , 1<=n<=1000000000

输出:

一个整数,代表有多少个牢房开着

 

 

一:第一种算法。 此算法是最基本的,就是一个环节一个环节地进行游戏。我们可以采用线性表来做,也可用一个大的数组来做。下下面我提供的前两种算法虽然所得答案是正确的,但代码在时间效率上和空间限制上都不符合要求。 3种算法是符合要求的。1):

//链表表示:

#include <stdio.h>

#include <stdlib.h>

typedef char ElemType;

typedef struct LNode

{

       ElemType data;

       struct LNode *next;

}LNode,*LinkList;

long dNum;

void creatList(LNode *S)

{

       LinkList stem=S,current;

       int i;

       for(i=1;i<=dNum;i++)

       {

              current=(LinkList)malloc(sizeof(LNode));

              current->data=0;

              current->next=NULL;

              stem->next=current;

              stem=stem->next;

       }

}

LinkList getElemaddress(LNode *S,int dPos)

 

{

       LNode *stem=S->next;

       int current;

       if(dPos>dNum||dPos<1)

       {

              printf("ERROR");

              return NULL;

       }

       else

       {

              for(current=1;current<dPos;current++)

              {

                     stem=stem->next;

              }

              return stem;

       }

}

void game(LinkList S,long d)

{

       long i=1,cur;

       LinkList p;

       do

       {

              cur=i*d;

              p=getElemaddress(S,cur);

              if(p->data==0)

                     p->data=1;

              else

                     p->data=0;

              i++;

              if(i*d>dNum)

                     break;

       }

       while (cur<=dNum);

}

             

long count(LinkList S)

{

       LinkList temp=S->next;

       long i,count=0;

       for(i=1;i<=dNum;i++)

       {

              if(temp->data==1)

                     count++;

              temp=temp->next;

       }

       return count;

}

 

void main()

{

       long i,countnum;

       LNode s,*p=&s;

       scanf("%d",&dNum);

       creatList(p);

       for(i=1;i<=dNum;i++)

       {

              game(p,i);                            //对于第I个牢房,进行游戏

       }

       countnum=count(p);                    //游戏结束后,进行统计。最后有多少个牢房是开头着的

       printf("%d/n",countnum);

}

 

2):   

用数组表示:

#include <stdio.h>

#define MAXSIZE 1000000000

int gameon(char *,int,int);                   //进行一轮游戏

void init_jail(char *,int);                      //初始化操作,置牢房为空

int JAIL=0;

int main()

{

       static char jail[MAXSIZE];

       int num,cur,num_of_openedjail;

       scanf("%d",&num);                     //num变量是牢房的个数

       init_jail(jail,num);           //num个牢房置空

       for(cur=1;cur<=num;cur++)

              num_of_openedjail=gameon(jail,cur,num);

       printf("%d/n",num_of_openedjail);

       return 0;

}

 

void init_jail(char *c,int i)

{

       int temp=1;

       for(;temp<=i;temp++)

              c[temp]=0;

}

 

int gameon(char *c,int i,int jail)

{

       int temp;

       for(temp=1;temp*i<=jail;temp++)

       {

              if(c[temp*i]==0)

              {

                     c[temp*i]=1;

                     JAIL++;

              }

              else

              {

                     c[temp*i]=0;

                     JAIL--;

              }

       }

       return JAIL;

}

 

 

二:第二种算法。

通过仔细分析我们可以发现,对于每个牢房,只要它的因数个数再加上一是奇数话,那么游戏结束后,它就是开着的。比如15它的第13515次游戏都可以对它进行操作,4次为偶数次操作,所以它到最后是关闭的。

算法的具体表示如下:

#include <stdio.h>

int com_yinshu_num(int a);

int main()

{

       int jail_num,cur,open_or_close,num_of_open=0;

       scanf("%d",&jail_num);

       for(cur=1;cur<=jail_num;cur++)

       {

              open_or_close=com_yinshu_num(cur)%2;   //判断操作的次数为奇数还是偶数

              if(open_or_close==1)

                     num_of_open++;

       }

       printf("%d/n",num_of_open);

       return 0;

}

 

int com_yinshu_num(int a)                                //求第个牢房的参与游戏的次数

{

       int temp=1,cur,i=1;

       cur=a/2;

       for(;temp<=cur;temp++)

       {

              if(a%temp==0)

                     i++;

       }

       return i;

}

 

三:第三种算法。

       如果我们能找到这种算法。那么这个题就显得很简单了。

大家研究一下3579。。。这个以3为首项,公差为2的等差数列和牢房的关系就会发现问题的关键所在了。

#include <stdio.h>

#include <math.h>

int main()

//此问题有个规律,就是所剩打开的牢房个数是 sqrt(1-jail_name)-1  向上取整

{

       int game;

       double jail_num;

       scanf("%lf",&jail_num);

       game=(int)ceil(sqrt(1+jail_num)-1);

       printf("%d/n",game);

       return 0;

}    

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值