0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
思路
使用链表模拟。
代码
#include <list>
class Solution {
public:
int lastRemaining(int n, int m) {
std::list<int> lister;
for(int i = 0;i < n;i++) {
lister.push_back(i);
}
auto cur = lister.begin();
while(lister.size() > 1) {
for(int i = 1;i < m;i++) {
cur++;
if(cur == lister.end()) {
cur = lister.begin();
}
}
auto tmp = cur;
cur++;
if(cur == lister.end()) {
cur = lister.begin();
}
lister.erase(tmp);
}
return lister.back();
}
};
思路二
数学.记f(n,m)表示最后剩下的数字,则删除一个数后,记f(n-1,m)表示最后剩下的数字,显然有f(n,m)=f'(n-1,m)。假设被删除的数下标为k,则k=(m-1)%n,对f'(n-1,m)序列作处理,每个数应用p(x)=(x-k-1)%n,则得到0,1,...,n-2序列,即f(n-1,m).而p(x)=(x-k-1)%n的逆操作为p-1(x)=(x+k+1)%n。所以有下面的推导式:
f(n,m)=f'(n-1,m)=[f(n-1,m)+k+1)]%n=[f(n-1,m)+(m-1)%n+1]%n=[f(n-1,m)+m]%n,省略中间式,最终结果为:
f(n,m)=[f(n-1,m)+m]%n,而这就是动态规划转移方程。使用动态规划,显然n=1,结果为0。推导过程从2到n。f(n,m)只和f(n-1,m)有关,使用一个变量表示即可。可以表示为 last = (last+m)%n。
代码
class Solution {
public:
int lastRemaining(int n, int m) {
int last = 0;
for(int i = 2;i <= n;i++) {
last = (last+m)%i;
}
return last;
}
};