首页 > 技术文章 > 约瑟夫环问题拓展 C/C++

Rane 2020-04-06 17:40 原文

拓展题目

若每次报到数字的人退出报数,且他后面的两个人也退出报数。当剩余玩家小于等于3个时,游戏结束,输出剩余玩家的原序号。

这样的情况该如何解决呢?

依照模拟的思路和代码:

详解约瑟夫环问题 C/C++


我们可以简单的对其进行拓展。我们需要考虑的是下面三种情况:

  • 1.三人位置连续且不经过队尾队首转换
  • 2.三人位置连续但要经过队尾队首转换
  • 3.三人位置不连续但不会经过队尾队首的转换
  • 4.三人位置不连续且存在队尾队首的转换

第一种情况下,我们需要考虑如何在不改动原迭代器位置的情况下把符合要求的三个人的状态设为不参与报数的状态。于是定义了一个新的等于原迭代器的迭代器temp,用它来将三人的状态改变。由于三人的位置相连且不经过队尾队首转换,所以只需要通过temp的自增便可以完成对三人状态的改变。

第二种情况下,我们需要考虑队尾队首的转换,即当 temp = s.end() 时,需要将temp = s.begin() 。

最后两种情况下,我们需要考虑的是如何找到报数人的后面两个参与报数的人,为了解决这个问题,我们可以写一个location函数,返回值类型为vector数组的迭代器。具体代码如下:

vector<bool>::iterator location(vector<bool>::iterator temp){
    while(!*temp){
        temp++;
        if(temp == s.end())
            temp= s.begin();
    }
    return temp;
}

写完该函数后,我们不难发现,前两种情况依然可以使用该函数完成对报数人后面两人的查找。因此四种情况的位置变换都可以归纳为一种情况,统一用上述函数完成。

除了报数人的状态值变为 0 ,temp的位置指向每变换一次,temp所指向的值就变为 0 ,进行两次位置变换和赋值,就完成了对三人状态的改变。最后,需要让原迭代器的指向和temp的最终指向相同,从而提高一点程序的效率。

我们来看看代码:

int main(){
    int m,n;
    cin>>n>>m;
    for(int q=0; q<n; q++)
        s.push_back(1);
    int dead(0), cnt(0);
    auto it=s.begin();
    while(dead < n-3){
        if(it == s.end())   it = s.begin();
        if(*it) cnt++;
        if(cnt == m){
            *it = 0;
            auto temp = it;
            temp = location(temp);
            *temp = 0;
            temp = location(temp);
            *temp = 0;
            it = temp;
            cnt = 0;
            dead+=3;
        }
        it++;
    }
    for(auto q=s.begin(); q!=s.end(); q++){
        if(*q){
            cout<<q-s.begin()+1<<' ';
        }
    }
    return 0;
}

最后对vector数组进行遍历,把还可以参与报数的人(即状态值为1的人)的原序号输出,便是题目所需要的答案。

推荐阅读