tc 147 2 PeopleCircle(再见约瑟夫环)

时间:2023-11-28 14:11:14

SRM 147 2 600PeopleCircle


Problem Statement

There are numMales males and numFemales females arranged in a circle. Starting from a given point, you count clockwise and remove the K'th person from the circle (where K=1 is the person at the current point, K=2 is the next person in the clockwise direction, etc...). After removing that person, the next person in the clockwise direction becomes the new starting point. After repeating this procedure numFemales times, there are no females left in the circle.

Given numMalesnumFemales and K, your task is to return what the initial arrangement of people in the circle must have been, starting from the starting point and in clockwise order.

For example, if there are 5 males and 3 females and you remove every second person, your return String will be "MFMFMFMM".

Definition

  • ClassPeopleCircle
  • Methodorder
  • Parametersint , int , int
  • Returnsstring
  • Method signaturestring order(int numMales, int numFemales, int K)
(be sure your method is public)

Limits

  • Time limit (s)2.000
  • Memory limit (MB)64

Constraints

  • numMales is between 0 and 25 inclusive
  • numFemales is between 0 and 25 inclusive
  • K is between 1 and 1000 inclusive

Test cases

    • numMales5
    • numFemales3
    • K2

    Returns"MFMFMFMM"

    Return "MFMFMFMM". On the first round you remove the second person - "M_MFMFMM". Your new circle looks like "MFMFMMM" from your new starting point. Then you remove the second person again etc.
    • numMales7
    • numFemales3
    • K1

    Returns"FFFMMMMMMM"

    Starting from the starting point you remove the first person, then you continue and remove the next first person etc. Clearly, all the females are located at the beginning. Hence return "FFFMMMMMMM"
    • numMales25
    • numFemales25
    • K1000

    Returns"MMMMMFFFFFFMFMFMMMFFMFFFFFFFFFMMMMMMMFFMFMMMFMFMMF"

    • numMales5
    • numFemales5
    • K3

    Returns"MFFMMFFMFM"

    Here we mark the removed people with '_', and the starting position with lower-case:
    Number of      | People Remaining
    Rounds | (in initial order)
    ---------------+-----------------
    0 | mFFMMFFMFM
    1 | MF_mMFFMFM
    2 | MF_MM_fMFM
    3 | MF_MM_FM_m
    4 | M__mM_FM_M
    5 | M__MM__m_M
    • numMales1
    • numFemales0
    • K245

    Returns"M"


This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder, Inc. All rights reserved.

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <typeinfo>
#include <fstream> using namespace std;
char s[] ;
vector<int> g ;
int n ;
int k ; void dead (int id , int ans , int m)
{
for (int i = ans ; i <= n ; i ++) {
k = m % i ;
id = (id + k) % i ;
}
// printf ("id = %d\n" , id ) ;
g.push_back (id) ;
} void solve (int fmale , int m)
{
dead ( , , m ) ;
for (int i = ; i <= n ; i ++) {
k = m % i ;
int id = ( + k) % i ;
if (id - < ) id = id - + i ;
else id -- ;
dead (id , i + , m ) ;
}
reverse (g.begin () , g.end ()) ;
// for (int i :g ) printf ("%d " , i) ; puts ("") ;
for (int i = ; i < n ; i ++) {
if (i < fmale) s[g[i]] = 'F' ;
else s[g[i]] = 'M' ;
}
s[n] = '\0' ;
} class PeopleCircle {
public:
string order(int male , int fmale , int m) {
// printf ("\nmale=%d\nfmale=%d\nm=%d\n" , male , fmale , m) ;
g.clear () ;
n = male + fmale ;
solve (fmale , m) ;
return s;
}
}; // CUT begin
ifstream data("PeopleCircle.sample"); string next_line() {
string s;
getline(data, s);
return s;
} template <typename T> void from_stream(T &t) {
stringstream ss(next_line());
ss >> t;
} void from_stream(string &s) {
s = next_line();
} template <typename T>
string to_string(T t) {
stringstream s;
s << t;
return s.str();
} string to_string(string t) {
return "\"" + t + "\"";
} bool do_test(int numMales, int numFemales, int K, string __expected) {
time_t startClock = clock();
PeopleCircle *instance = new PeopleCircle();
string __result = instance->order(numMales, numFemales, K);
double elapsed = (double)(clock() - startClock) / CLOCKS_PER_SEC;
delete instance; if (__result == __expected) {
cout << "PASSED!" << " (" << elapsed << " seconds)" << endl;
return true;
}
else {
cout << "FAILED!" << " (" << elapsed << " seconds)" << endl;
cout << " Expected: " << to_string(__expected) << endl;
cout << " Received: " << to_string(__result) << endl;
return false;
}
} int run_test(bool mainProcess, const set<int> &case_set, const string command) {
int cases = , passed = ;
while (true) {
if (next_line().find("--") != )
break;
int numMales;
from_stream(numMales);
int numFemales;
from_stream(numFemales);
int K;
from_stream(K);
next_line();
string __answer;
from_stream(__answer); cases++;
if (case_set.size() > && case_set.find(cases - ) == case_set.end())
continue; cout << " Testcase #" << cases - << " ... ";
if ( do_test(numMales, numFemales, K, __answer)) {
passed++;
}
}
if (mainProcess) {
cout << endl << "Passed : " << passed << "/" << cases << " cases" << endl;
int T = time(NULL) - ;
double PT = T / 60.0, TT = 75.0;
cout << "Time : " << T / << " minutes " << T % << " secs" << endl;
cout << "Score : " << * (0.3 + (0.7 * TT * TT) / (10.0 * PT * PT + TT * TT)) << " points" << endl;
}
return ;
} int main(int argc, char *argv[]) {
cout.setf(ios::fixed, ios::floatfield);
cout.precision();
set<int> cases;
bool mainProcess = true;
for (int i = ; i < argc; ++i) {
if ( string(argv[i]) == "-") {
mainProcess = false;
} else {
cases.insert(atoi(argv[i]));
}
}
if (mainProcess) {
cout << "PeopleCircle (600 Points)" << endl << endl;
}
return run_test(mainProcess, cases, argv[]);
}
// CUT end

题意:n个人围成一圈,从编号为0的人开始报数1,后面的依次报2,3……当报道m时,该人离开圈子,他后面的一个人重新从1开始报数,以此类推……游戏最终会只有一个人留下来。这道题中我们需要得到的是每次离开之人的编号。(总人数n已知,m已知)。-------- 约瑟夫环问题,几乎是每个acmer的入门套餐。

上学期的时候,貌似用链表模拟做过,后来又用数学方法求过,但并不是很理解,如今又碰到了,便打算好好写份约瑟夫环报告。

从第一轮开始:

0 1 2 3 4 5 …… (n - 2)   (n - 1) 现在有n个人

明显第一次出队的人编号为m%n-1,我们令k = m % n ;

那么重新排列后为:

              k  k +1 k+2…… n-1     0     1     2      3    ……  k-2    

再把他们以0~n-1编号: 0  1   2   n-k-1   n-k  n-k+1  n-k+2 n-k+3     n-2

通过上下比对,我们能隐约发现前后存在一种映射关系,大概是(假设重新编号后,有个下标为id) id + k  ;

总之在算一下,其实是(id + k) % n ;

那么我们是不是能很容易的知道,现在的每个人在上一轮中存在的位置了吗?

求出每次游戏最后留下来的人是谁:

 #include<bits/stdc++.h>
int beg , m , n ;//从第beg个人开始数1,数到m的人离开,总人数为n。把他们从0~n -1编号。
int k ; void solve ()
{
int cur = ;
for (int i = ; i <= n ; i ++) {
k = m % i ;
cur = (k + cur ) % i ;
}
cur = (cur + beg ) % n ;
printf ("%d\n" , cur ) ;
} int main ()
{
while (~ scanf ("%d%d%d" , &beg , &m , &n) ) {
solve () ;
}
return ;
}

通过这个映射关系,我们还能求出:

每一轮出圈的人的编号。

 #include<bits/stdc++.h>
int m , n ;
int k ;
std::vector<int> g ; void dead (int id , int ans)
{
for (int i = ans ; i <= n ; i ++) {
k = m % i ;
id = (id + k) % i ;
}
g.push_back (id) ;
} void solve ()
{
dead ( , ) ;
for (int i = ; i <= n ; i ++) {
k = m % i ;
int id = ( + k) % i ;
id -- ;
if (id < ) id += i ;
dead (id , i + ) ;
}
std::reverse (g.begin () , g.end ()) ;
for (int i : g) printf ("%d," , i) ; puts ("") ;
} int main ()
{
while (~ scanf ("%d%d" , &m , &n)) {
g.clear () ;
solve () ;
}
return ;
}

看不懂的话,再结合这篇博文(orz):http://blog.csdn.net/u012333003/article/details/27076603