您的位置:首页 > 其它

UVALive - 3882 And Then There Was One (递推[dp])

2016-09-05 21:21 399 查看
大体题意:

给你一个由n 个数(1~n)组成的圆环,刚开始删除m,以后每数k 个数删除一个数,求最后剩下哪个数?

思路:

这个题数据量比较大,什么数组模拟,链表模拟, 都会超时!

这个题目不就是猴子选大王吗= =!

看了看相关约瑟夫问题的讲解,感觉很巧妙!简单记录一下:

有这么一类问题:

给你1~n 的圆环,你从1报数,报到m 停止删除这个数,继续报,求最后的人?

因为涉及到取余问题,可以改变一下数据,给你一个0~n-1 的圆环,你从0报数,报到m-1停止,删除这个数,继续报,求最后的人?

这两个问题是一样的!

第一次报数肯定是m%n 这个位置就退出了! 我们令 k = m % n

那么剩下的人就是:

k   k+1   k+2 .... n-1  0  1  2  ... k-2

我们可以给他们重新编号:

k--->0

k+1 ---> 1

k+2 ---->2

k-2 ----->n-2

这样只剩下了n-1个人,假设我们知道这n-1个人的结果  为dp[n-1]的话,那么很容易推出  n 个人是多少了!

观察数据也可以知道  dp
= (dp[n-1] + k) % n

而dp[1] = 0  (显然),因此这个递推式就有了!

这样dp
表示的是   从0~n-1 这个n个人报数  报到k删除 继续的结果!!

然而这个题目 是 一开始就删除了m  ,  这就相当于  从m-k+1开始报数

也就是由原来的0 ---> m-k+1

因此我们要给dp
+= m-k+1适合这个题目!

注意取模问题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10000 + 10;
int dp[maxn];
int main(){
int n,k,m;
while(scanf("%d %d %d",&n, &k, &m) == 3 && (n || k || m)){
dp[1] = 0;
for (int i = 2; i <= n; ++i){
dp[i] = (dp[i-1] % i + k % i) % i;
}
dp
= (dp
%n + (m-k+1) % n) % n;
if (dp
<= 0)dp
+= n;
printf("%d\n",dp
);
}
return 0;
}
/*
8 5 3
100 9999 98
10000 10000 10000
0 0 0
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: