08-1. Talent and Virtue
About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about people's talent and virtue. According to his theory, a man being outstanding in both talent and virtue must be a "sage(圣人)"; being less excellent but with one's virtue outweighs talent can be called a "nobleman(君子)"; being good in neither is a "fool man(愚人)"; yet a fool man is better than a "small man(小人)" who prefers talent than virtue.
Now given the grades of talent and virtue of a group of people, you are supposed to rank them according to Sima Guang's theory.
Input Specification:
Each input file contains one test case. Each case first gives 3 positive integers in a line: N (<=105), the total number of people to be ranked; L (>=60), the lower bound of the qualified grades -- that is, only the ones whose grades of talent and virtue are both not below this line will be ranked; and H (<100), the higher line of qualification -- that is, those with both grades not below this line are considered as the "sages", and will be ranked in non-increasing order according to their total grades. Those with talent grades below H but virtue grades not are cosidered as the "noblemen", and are also ranked in non-increasing order according to their total grades, but they are listed after the "sages". Those with both grades below H, but with virtue not lower than talent are considered as the "fool men". They are ranked in the same way but after the "noblemen". The rest of people whose grades both pass the L line are ranked after the "fool men".
Then N lines follow, each gives the information of a person in the format:
ID_Number Virtue_Grade Talent_Gradewhere ID_Number is an 8-digit number, and both grades are integers in [0, 100]. All the numbers are separated by a space.
Output Specification:
The first line of output must give M (<=N), the total number of people that are actually ranked. Then M lines follow, each gives the information of a person in the same format as the input, according to the ranking rules. If there is a tie of the total grade, they must be ranked with respect to their virtue grades in non-increasing order. If there is still a tie, then output in increasing order of their ID's.
Sample Input:14 60 80 10000001 64 90 10000002 90 60 10000011 85 80 10000003 85 80 10000004 80 85 10000005 82 77 10000006 83 76 10000007 90 78 10000008 75 79 10000009 59 90 10000010 88 45 10000012 80 100 10000013 90 99 10000014 66 60Sample Output:
12 10000013 90 99 10000012 80 100 10000003 85 80 10000011 85 80 10000004 80 85 10000007 90 78 10000006 83 76 10000005 82 77 10000002 90 60 10000014 66 60 10000008 75 79 10000001 64 90
这一题前面的导言有点误导人。我在这里说明下题目的要求。题目里给出了两个分数L和H,只有 virtue 和 talent 分数都不低于L的才会进入排名中。两个分数都不低于H的是 sages ;之后是 noblemen ,他们的 virtue 分数达到了H,但是 talent 分数低于H;再后面的是 fool men,他们的两个分数都低于H,并且 virtue 分数不低于 talent 分数;剩下的人排在最后,他们当中包括两个分数都没到H,并且 talent 分数高于 virtue 的人,以及 talent 分数达到H,但 virtue 分数低于H的人。在这四组成绩中,每个组内部按照总分从高到低的顺序排列;如果总分一样,就把 virtue 分高的排前面;如果 virtue 分也一样,说明他们的 talent 分也一样,这时就按他们的ID号从低到高排列。
把这些关系搞清楚了,就可以排序了。这一题很明显非常适合用桶排序。我的方法是,建立 sage, noble, fool, small 四个桶,这里我就用 small 表示最后一组的人了。在读入数据时,先把所有人按情况放入某个桶内,然后再对每个桶分别进行排序。我用了 vector 模版来表示桶,然后调用STL里的 sort 函数进行排序,这里我重载了 < 运算符。为了防止会超时,我把 cin 和 cout 的输入输出方式换成了 scanf 和 printf 。如果做一下测试,会发现 cin 和 cout 真的会比 scanf 和 printf 慢很多。下面是完整的代码:
#include <iostream>
#include "stdio.h"
#include <vector>
#include <algorithm>
using namespace std;
int N, L, H;
int Msage=0, Mnoble=0, Mfool=0, Msmall=0;
struct student
{
int id;
int virtue;
int talent;
int total;
};
bool operator < (const student &stu1, const student &stu2)
{
if (stu1.total>stu2.total)
return true;
else if (stu1.total==stu2.total)
if (stu1.virtue>stu2.virtue)
return true;
else if (stu1.virtue==stu2.virtue)
if (stu1.id<stu2.id)
return true;
return false;
}
int main()
{
cin >> N >> L >> H;
vector<student> sage, noble, fool, small;
for (int i=0; i<N; ++i)
{
student tmp;
scanf("%d %d %d", &tmp.id, &tmp.virtue, &tmp.talent);
tmp.total = tmp.virtue + tmp.talent;
if (tmp.virtue<L || tmp.talent<L)
continue;
if (tmp.virtue>=H && tmp.talent>=H)
{
sage.push_back( tmp );
++Msage;
}
else if (tmp.talent>=L && tmp.virtue>=H)
{
noble.push_back( tmp );
++Mnoble;
}
else if (tmp.talent<H && tmp.virtue<H && tmp.virtue>=tmp.talent)
{
fool.push_back( tmp );
++Mfool;
}
else
{
small.push_back( tmp );
++Msmall;
}
}
sort( sage.begin(), sage.end() );
sort( noble.begin(), noble.end() );
sort( fool.begin(), fool.end() );
sort( small.begin(), small.end() );
cout << Msage+Mnoble+Mfool+Msmall;
for (int i=0; i<Msage; ++i)
printf("\n%d %d %d", sage[i].id, sage[i].virtue, sage[i].talent);
for (int i=0; i<Mnoble; ++i)
printf("\n%d %d %d", noble[i].id, noble[i].virtue, noble[i].talent);
for (int i=0; i<Mfool; ++i)
printf("\n%d %d %d", fool[i].id, fool[i].virtue, fool[i].talent);
for (int i=0; i<Msmall; ++i)
printf("\n%d %d %d", small[i].id, small[i].virtue, small[i].talent);
return 0;
}
08-2. The World's Richest
Forbes magazine publishes every year its list of billionaires based on the annual ranking of the world's wealthiest people. Now you are supposed to simulate this job, but concentrate only on the people in a certain range of ages. That is, given the net worths of N people, you must find the M richest people in a given range of their ages.
Input Specification:
Each input file contains one test case. For each case, the first line contains 2 positive integers: N (<=105) - the total number of people, and K (<=103) - the number of queries. Then N lines follow, each contains the name (string of no more than 8 characters without space), age (integer in (0, 200]), and the net worth (integer in [-106, 106]) of a person. Finally there are K lines of queries, each contains three positive integers: M (<= 100) - the maximum number of outputs, and [Amin, Amax] which are the range of ages. All the numbers in a line are separated by a space.
Output Specification:
For each query, first print in a line "Case #X:" where X is the query number starting from 1. Then output the M richest people with their ages in the range [Amin, Amax]. Each person's information occupies a line, in the format
Name Age Net_WorthThe outputs must be in non-increasing order of the net worths. In case there are equal worths, it must be in non-decreasing order of the ages. If both worths and ages are the same, then the output must be in non-decreasing alphabetical order of the names. It is guaranteed that there is no two persons share all the same of the three pieces of information. In case no one is found, output "None".
Sample Input:
12 4 Zoe_Bill 35 2333 Bob_Volk 24 5888 Anny_Cin 95 999999 Williams 30 -22 Cindy 76 76000 Alice 18 88888 Joe_Mike 32 3222 Michael 5 300000 Rosemary 40 5888 Dobby 24 5888 Billy 24 5888 Nobody 5 0 4 15 45 4 30 35 4 5 95 1 45 50Sample Output:
Case #1: Alice 18 88888 Billy 24 5888 Bob_Volk 24 5888 Dobby 24 5888 Case #2: Joe_Mike 32 3222 Zoe_Bill 35 2333 Williams 30 -22 Case #3: Anny_Cin 95 999999 Michael 5 300000 Alice 18 88888 Cindy 76 76000 Case #4: None
这一题乍看之下还是桶排序,但实际上并没有那个简单。桶是什么?可以按照年龄来分桶,最后输出时,其实又是一个归并排序。这样操作其实还是比较麻烦。也可以在题目要求的年龄范围内排序,并且只排出前几名的就可以了。这两种方法都得自己写出排序算法。而我用的方法非常简单,先给所有的人一次性排好序,然后从前到后给出年龄范围内的人。至于排序算法,依然是调用STL中的 sort 函数,并且重载 < 运算符。STL里只会用到 < 符号。下面是程序代码:
#include <iostream>
#include "stdio.h"
#include <vector>
#include <algorithm>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int N, K;
bool none;
struct people
{
string name;
int age;
int worth;
};
bool operator < (const people &p1, const people &p2)
{
if (p1.worth>p2.worth)
return true;
else if (p1.worth==p2.worth)
if (p1.age<p2.age)
return true;
else if (p1.age==p2.age)
if (p1.name<p2.name)
return true;
return false;
}
int main()
{
cin >> N >> K;
people a[N];
for (int i=0; i<N; ++i)
{
cin >> a[i].name >> a[i].age >> a[i].worth;
}
std::sort( a, a+N );
int num, la, sa, count;
for (int quire=1; quire<=K; ++quire)
{
count = 0;
cin >> num >> la >> sa;
printf("Case #%d:\n", quire);
for (int i=0; i<N; ++i)
{
if (a[i].age>=la && a[i].age<=sa)
{
cout << a[i].name << ' ' << a[i].age << ' ' << a[i].worth << endl;
++count;
}
if (count==num)
break;
}
if (count==0)
cout << "None\n";
}
return 0;
}
08-3. Sort with Swap(0,*)
Given any permutation of the numbers {0, 1, 2,..., N-1}, it is easy to sort them in increasing order. But what if Swap(0, *) is the ONLY operation that is allowed to use? For example, to sort {4, 0, 2, 1, 3} we may apply the swap operations in the following way:
Swap(0, 1) => {4, 1, 2, 0, 3}
Swap(0, 3) => {4, 1, 2, 3, 0}
Swap(0, 4) => {0, 1, 2, 3, 4}
Now you are asked to find the minimum number of swaps need to sort the given permutation of the first N nonnegative integers.
Input Specification:
Each input file contains one test case, which gives a positive N (<=105) followed by a permutation sequence of {0, 1, ..., N-1}. All the numbers in a line are separated by a space.
Output Specification:
For each case, simply print in a line the minimum number of swaps need to sort the given permutation.
Sample Input:10 3 5 7 2 6 4 9 0 8 1Sample Output:
9
输入的第一个数表示数据个数,并不是序列里的数。关于这一题姥姥给出了分析,可以数一数有多少个单元环和多元环,然后直接通过公式计算出来。不过像我们这样的小白,肯定想不出这样高大上的方法,所以还是实际来交换看看到底需要多少步。实际交换时有这样一个问题,比如我要交换0和7,那么我必须要在数组里找到0和7。如果每次都把数组遍历一遍,显然会耽误时间。姥姥的做法是再开一个数组为门作为索引,只是这样做会多消耗一些内存。我的做法是,把数组的下标和元素值反过来表示。例如 a[1]=3,它并不是表示第1个元素是3,而是第3个元素的值为1,或者说值为1的元素在第3个位置。这样一来,当我要找元素k时,通过a[k]就可以知道它的下标是多少。当然交换某两个数组元素时,其实就是交换原始数据里那两个数的下标,与原始的交换两个数的效果一样。在交换时,由于每次只用0来交换,所以当某个环里不含0时,就必须先把0交换进去。在某个环内交换时,并不需要真的每次都拿0来交换,只需要像表排序那样把每个元素挪到正确位置就可以了。下面是完整的代码:
#include <iostream>
using namespace std;
int main()
{
int N, k, tmp;
int* a;
cin >> N;
a = new int [N];
for (int i=0; i<N; ++i)
{
cin >> k;
a[k] = i;
}
int num = 0;
for (int i=0; i<N; ++i)
{
if (a[i]==i)
continue;
if (i!=0)
{
swap( a[0], a[i] );
++num;
}
int j = 0;
while (a[j]!=j)
{
tmp = a[j];
a[j] = j;
j = tmp;
++num;
}
--num;
}
cout << num;
delete [] a;
return 0;
}