这个问题是这样的:
时限: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它的第1、3、5、15次游戏都可以对它进行操作,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;
}
三:第三种算法。
如果我们能找到这种算法。那么这个题就显得很简单了。
大家研究一下3、5、7、9。。。这个以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;
}