递推法求解约瑟夫问题

来源:互联网 发布:淘宝详情页模板怎么做 编辑:程序博客网 时间:2024/06/07 21:11


递推法求解约瑟夫问题


假设有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

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


/**========================================== *   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 0
原创粉丝点击