PKUACM1012 约瑟夫环

        问题描述:k个好人和k个坏人站成一圈,前k个是好人,后k个是坏人,m是隔的人数,求最小的m,可以将所有的k个坏人杀死,而好人留下。
       这是一个典型的约瑟夫问题的简单变形,在做这道题的时候,我对约瑟夫问题有所了解,但是对具体的算法步骤却不是很通,我的第一想法就是用循环链表来做。

这是我的程序:

 1#include<stdio.h>
 2
 3struct node{
 4    int x;
 5    int flag;
 6    struct node* next;
 7    }
;
 8typedef struct node NODE;
 9int a[14]={0};
10
11
12void main()
13{
14    int k;
15    NODE *p;
16    NODE *q;
17    int i;
18    while(scanf("%d",&k)==1)
19    {
20        if(k==0)
21        break;
22        if(a[k]==0){
23        int tem=0;
24        int m=k+1;
25        NODE *con=NULL;
26        NODE *con1=NULL;
27        NODE *head=(NODE *)malloc(sizeof(NODE));
28        head->x=1;
29        head->flag=0;
30        q=head;
31        for(i=2;i<=k;i++)
32        {
33          p=(NODE *)malloc(sizeof(NODE));  //分配指针
34          if(p==NULL)
35            return;
36          p->x=i;
37          p->flag=0;
38          q->next=p;
39          q=p;
40        }

41        con=q;
42        con1=q;
43        for(i=k+1;i<=2*k;i++)
44        {
45          p=(NODE *)malloc(sizeof(NODE));  //分配指针
46          if(p==NULL)
47            return;
48          p->x=i;
49          p->flag=1;
50          q->next=p;
51          q=p;
52        }

53
54        q->next=head;
55        q=con->next;
56         while(m)
57         {
58            while(q->flag==1)//是坏人
59            {
60                q->flag=2;
61                tem++;
62
63                for(i=1;i<=m;i++)
64                {
65                    q=q->next;
66                    if(q->flag==2)
67                    {
68                        while(q->flag==2)
69                        { q=q->next;}
70                    }

71                }

72
73            }

74            if(tem==k)
75              break;
76              m++;
77            tem=0;
78            con=con->next;
79            q=con->next;
80            NODE *con2=con1->next;
81            for(i=k+1;i<=2*k;i++)
82            {
83                if(con2->flag==2)
84                  con2->flag=1;
85                con2=con2->next;
86            }

87         }

88         a[k]=m;
89         printf("%d\n",m);
90        }

91        else printf("%d\n",a[k]);
92
93    }

94    }

95
        现在看来当时的程序真是挺臃肿的,这是我的直观想法,每一个节点就是一个结构体,里面有该节点的值和标记它是好人还是坏人的flag,以及指向下一个节点的指针,开始杀人时就循链检查,发现标记为0的是好人,说明当前m不合适,退出m加1,开始新一轮的搜索,否则继续找下一个。在开始新的一轮的搜索时,一定要还原原来的链,否则会出错,我原来是直接删掉节点了,后来发现无法恢复,就改成了将标记改为2。恢复的时候再改过来就行。这种方法很直观,但是效率很低,算到10就不行了。
        后来,我上网搜索了一下别人的代码,发现用的是很巧妙的数学方法,根本连数组都不用建,真慨叹人类的智慧啊!
 1#include<stdio.h>
 2
 3int main()
 4{
 5    int a[14]={0};
 6    int k=0,m=0;
 7    while(scanf("%d",&k)==1)
 8    {
 9        if(k==0)
10         break;
11        if(a[k]==0)
12        {
13           for(m=k+1;;m++)
14           {
15               int n=2*k;
16               int sp=0;
17               while(n>k)
18               {
19                   sp=(sp+m-1)%n;
20                   if(sp<k)
21                    break;
22                    n--;
23                   }

24                if(n==k)
25                {
26                    a[k]=m;
27                    printf("%d\n",a[k]);
28                    break;
29                }

30            }

31        }

32        else printf("%d\n",a[k]);
33        }

34  return 0;
35    }

36
        整整比我的缩短了快两倍,简单的数字就可以判断了,程序也很好理解,执行速度也很快。在遇到有环的时候,可以试试sp=(sp+m-1)%n的访问形式,简单高效。
        除此之外,这道题在做的时候,要注意重复输入的问题,可以将之前计算的结果保存下来,遇到重复输入了就直接检查数组输出,又可以节省时间了。
        还有一种更暴力的方法:打表。因为输入的k,0<k<14,所以可以私底下直接算出k=1,2.......13的值,直接switch选择输出,,有点白痴了。

posted on 2010-12-18 00:47 Bamboo 阅读(711) 评论(0)  编辑 收藏 引用 所属分类: ACM题目解析


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2010年12月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜