递推法求解约瑟夫问题


递推法求解约瑟夫问题


假设有n个人围成一圈,编号为0,1,2,3,...,n-1,现在从编号为0的人开始 1到k 报数,报k的人退出圈子,下一个人继续从1到k报数。直到所有人退出圆圈。

问:最后一个退出圆圈的人编号是多少?

求解:
利用递推法:

1.起点(边界):  考虑人数为1的情况

因为只有编号为0的人,所以最后出去的一定是0
dp[1]=0;

2.假设已经求出人数为x-1的情况 (即最后出去人的编号),现在我要考虑求解人数为x的情况。


我们先分析一下,从x个人中退出x个人的过程:

1.先退出1个人

首先编号为0到k-1的人报数,k-1退出

2.再退出x-1个人

之后再退出剩下的x-1个人

这个时候就可以写出状态专移方程了!!

dp[x]=(d[x-1]+k)%x;

为什么这样的?因为剔除了1个人(就是k-1)之后,剩下x-1个人,将k作为编号0,那么dp[x-1]就是x-1情形时的最后一人,也就是所求(也就是x情形时的最后一人)
那么我们只需将dp[x-1]在x-1情形下的编号转化为x情形下的编号即可,即+k。
别忘了取模!






1.uva 1394 - And Then There Was One

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=464&page=show_problem&problem=4140


/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source£ºuva 1394 - And Then There Was One
 *   @type:  dp
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn = 10000;
int n,k,m;
int dp[maxn+5];
int main()
{
    while(~scanf("%d%d%d",&n,&k,&m)&&(n||k||m) )
    {
        dp[1]=0;
        for(int i=2;i<n;i++)
        {
            dp[i]=(dp[i-1]+k)%i;
        }
        dp[n]=  (dp[n-1]+m )%n;

        printf("%d\n",dp[n]+1);

    }

   return 0;
}


2.uva  1452 - Jump


/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source£ºuva 1452- Jump
 *   @type:  dp
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
int ans[4];
bool vis[3];
int n,k;
int main()
{
    int T;scanf("%d",&T);

    while(T--)//ans[k]表示倒数第k个
    {
        scanf("%d%d",&n,&k);
        ans[1]=0;

        memset(vis,0,sizeof vis);
        ans[1]= (ans[1]+k)%2;
        vis[ ans[1] ]=1;
        ans[2]=vis[0]?1:0;


        memset(vis,0,sizeof vis);
        ans[1]=(ans[1]+k)%3;
        ans[2]=(ans[2]+k)%3;
        vis[ ans[1] ]=1;
        vis[ ans[2] ]=1;
        for(int i=0;i<3;i++)  if(!vis[i])
        {
            ans[3]=i;
            break;
        }

        for(int i=4;i<=n;i++)
        {
            ans[1]=( ans[1]+k)%i;
            ans[2]=( ans[2]+k)%i;
            ans[3]=( ans[3]+k)%i;
        }
        ans[1]++,ans[2]++,ans[3]++;
        printf("%d %d %d\n",ans[3],ans[2],ans[1]);



    }

   return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值