题目链接:(中英文都有,题目完全等价,代码可通用)
1015 德才论 (25分)
1062 Talent and Virtue (25分)
给出N个学生的 ID、德分、才分,进行综合排序,排序按以下4步优先级进行:
1.先按分类mark等级降序排列(不及格者直接剔除)
分类如下表:(分数为整数,用区间表达)
优先级(数字大优先) | 分类mark | 德分(=V) | 才分(=T) |
3 | 圣人 | [H,100] | [H,100] |
2 | 君子 | [H,100] | [L,H) |
1 | 愚人 | [L,H) (德≥才) | [L,V] |
0 | 小人 | [L,T) (德<才) | [L,100) (易错!) |
-1(剔除) | 不及格 | [0,L) | [0,L) |
要特别注意的是!才分优良,但德分不优者算小人。所以建议先剔除不合格者,再按德分是否优良分两大类,再细分。
2.同等级者按总分降序排序
3.同等级同总分者按德分降序排序
4.同分数者按ID升序排序(最后这个是升序!)
分数和分类mark用共用体组成32位value值降序,当value同则ID按升序排,分两次32位比较
(使用共用体版本。使用共用体要小心,特别写入容易出错,不懂的请自行查找资料或在评论区留言)
此版本的结构体位表如下:
成员名 | talent才分 | virtue德分 | total总分 | mark优先级 | ID |
所在比特位 | 0~7 | 8~15 | 16~23 | 24~31 | 32~64 |
最大整数范围\实际使用范围 | 256 \ 101 | 256 \ 101 | 256 \ 201 | 256 \ 4 | 2^32 \ 10^8 |
C++代码如下:
//1062 Talent and Virtue (25分)
#include
#include
#include
using namespace std;
typedef unsigned int U32;
struct INFO {//INFO
union { //匿名共用体:由一个匿名结构体{talent, virtue, total, mark}和value共用32比特空间。
U32 value;
struct{ //talent:才分;virtue:德分;total:总分;mark:分类标记
char talent, virtue, total, mark;
}; //匿名结构体,熟练后一点也不麻烦
};
U32 ID;
INFO(U32 ID, int virtue, int talent, int mark)
:ID(ID), virtue(virtue), talent(talent), total(virtue + talent), mark(mark) {}
bool operator<(const INFO&b) {
if (value == b.value)return ID < b.ID;
return value > b.value;
}//用于sort(),按value降序排序,相当于按 mark,total,virtue优先顺序降序。value同则按ID升序。
};
int main() {
int N, L, H; //人数,及格下限,优良下限
scanf("%d %d %d", &N, &L, &H);
vector A; A.reserve(N); //预留容量为N,避免扩容浪费时间
for (size_t i = 0; i < N; i++) {
U32 ID; int virtue, talent;
scanf("%u %d %d", &ID, &virtue, &talent);
if (virtue < L || talent < L)continue; //排除不合格者①
if (virtue >= H) { //德优
A.emplace_back(ID, virtue, talent, (talent >= H) + 2); //当(talent >= H): 为真表示圣人 mark=3;为假表示君子 mark=2
}else { //L ≤ virtue < H ,L ≤ talent
A.emplace_back(ID, virtue, talent, (virtue >= talent)); //当(virtue >= talent):为真表示愚人mark=1;为假表示小人mark=0
}
}
sort(A.begin(), A.end());
printf("%d\n", A.size());
for (auto&a : A) { //注意ID取反(8位数字)
printf("%08u %d %d\n", a.ID, a.virtue, a.talent);
}
system("pause");
return 0;
}
笔者在网上搜不到比这更简短的此题AC代码了,不要看注释可以用正则表达式【//.*$】替换为【】(空字符串)去掉注释。
成员名 | notID | talent才分 | virtue德分 | total总分 | mark优先级 |
所在比特位 | 0~31 | 32~39 | 40~47 | 48~55 | 56~63 |
最大整数范围\实际使用范围 | 2^32 \ 10^8 | 256 \ 101 | 256 \ 101 | 256 \ 201 | 256 \ 4 |
其中 notID 是ID的按位取反,以此达到整体降序排序时,ID是升序排序的效果。但要注意输出时要再次取反。
只要按整个结构体所占的64位无符号整型的大小降序排序,就能达到题目要求的排序效果!
如果是64位系统,理论上此方法执行速度更快!
C++代码如下:
//1062 Talent and Virtue (25分)
#include
#include
#include
using namespace std;
typedef unsigned int U32;
typedef unsigned long long U64;
union INFO {//INFO共用体 由一个匿名结构体{notID,talent, virtue, total, mark}和value共用64比特空间。
struct { //先声明的在低位
U32 notID; //ID的按位取反,如此可以对整体降序达到对ID升序的效果
char talent, virtue, total, mark; //mark在最高位
};//talent:才分;virtue:德分;total:总分;mark:分类标记
U64 value; //相当于 value=(notID<<32)|(mark<<24)|(total<<16)|(virtue<<8)|talent;
INFO(U32 ID, int virtue, int talent, int mark)
:notID(~ID), virtue(virtue), talent(talent), total(virtue + talent), mark(mark) {}
bool operator<(const INFO&b) {
return value > b.value;
}//用于sort(),按value降序排序,相当于按 mark,total,virtue优先顺序降序,而notID是ID取反,即相当于ID升序排序。
};
int main() {
int N, L, H; //人数,及格下限,优良下限
scanf("%d %d %d", &N, &L, &H);
vector A; A.reserve(N); //预留容量为N,避免扩容浪费时间
for (size_t i = 0; i < N; i++) {
U32 ID; int virtue, talent;
scanf("%u %d %d", &ID, &virtue, &talent);
if (virtue < L || talent < L)continue; //排除不合格者①
if (virtue >= H) { //德优
A.emplace_back(ID, virtue, talent, (talent >= H) + 2); //当(talent >= H): 为真表示圣人 mark=3;为假表示君子 mark=2
}
else { //L ≤ virtue < H ,L ≤ talent
A.emplace_back(ID, virtue, talent, (virtue >= talent)); //当(virtue >= talent):为真表示愚人mark=1;为假表示小人mark=0
}
}
sort(A.begin(), A.end());
printf("%d\n", A.size());
for (auto&a : A) { //注意ID取反(8位数字)
printf("%08u %d %d\n", ~a.notID, a.virtue, a.talent);
}
system("pause");
return 0;
}
此代码与版本1的32位解法基本上只有INFO结构体和最后输出不同。不信自己比较。
执行时间仅51ms!(执行时间受服务器“心情”影响会有波动,故仅供参考)
此版本不需要掌握共用体和计算机组成原理中整型的存储原理,不需要理会数据范围,ID用string字符串暴力存储。容易上手,就是要多写几个if,执行速度会慢一些。
#include
#include
#include
#include
using namespace std;
struct INFO {//INFO
int virtue, total, mark;//talent:才分;virtue:德分;total:总分;mark:分类标记
string ID;
INFO(const char *ID, int virtue, int talent, int mark)
:ID(ID), virtue(virtue), total(virtue + talent), mark(mark) {}
bool operator<(const INFO&b) {
if (mark != b.mark)return mark > b.mark;
if (total != b.total)return total > b.total;
if (virtue != b.virtue)return virtue > b.virtue;
return ID < b.ID; //ID是升序
}//用于sort(),按 mark,total,virtue优先顺序降序。最后按ID升序。
int talent()const { return total - virtue; } //可以用总分倒推才分
};
int main() {
int N, L, H; //人数,及格下限,优良下限
scanf("%d %d %d", &N, &L, &H);
vector A; A.reserve(N); //预留容量为N,避免扩容浪费时间
for (size_t i = 0; i < N; i++) {
char ID[10]; int virtue, talent;
scanf("%s %d %d", ID, &virtue, &talent);
if (virtue < L || talent < L)continue; //排除不合格者①
if (virtue >= H) { //德优
A.emplace_back(ID, virtue, talent, (talent >= H) + 2); //当(talent >= H): 为真表示圣人 mark=3;为假表示君子 mark=2
}else { //L ≤ virtue < H ,L ≤ talent
A.emplace_back(ID, virtue, talent, (virtue >= talent)); //当(virtue >= talent):为真表示愚人mark=1;为假表示小人mark=0
}
}
sort(A.begin(), A.end());
printf("%d\n", A.size());
for (auto&a : A) { //注意ID取反(8位数字)
printf("%s %d %d\n", a.ID.data(), a.virtue, a.talent());
}
system("pause");
return 0;
}
如下C语言代码中,由于C语言的qsort()需要代入一个返回int型的比较函数指针,不方便直接用64位整型算出,不如将ID单独拿出来比较。
此版本的结构体位表如下:
成员名 | talent才分 | virtue德分 | total总分 | mark优先级 | ID |
所在比特位 | 0~7 | 8~15 | 16~23 | 24~31 | 32~64 |
最大整数范围\实际使用范围 | 256 \ 101 | 256 \ 101 | 256 \ 201 | 256 \ 4 | 2^32 \ 10^8 |
C语言参考代码如下(用qsort,写cmp函数代入比较):
#include
#include
typedef unsigned char U8;
typedef unsigned int U32;
#define MAX_N 100000
typedef struct {//INFO由{rvID,talent, virtue, total, mark}成员组成共用64比特空间。
//talent:才分;virtue:德分;total:总分;mark:分类标记
U8 talent, virtue, total, mark; //mark在最高位
U32 ID; //ID在高位
}INFO;
int cmp(const void*a, const void*b) {
if (*((U32*)b) == *((U32*)a)) //低位的分数比较值相同,则比较ID
return ((INFO*)a)->ID - ((INFO*)b)->ID; //升序是 a-b ;降序 b-a 。a指左参数。
return *((U32*)b) - *((U32*)a); //不同返回低位的分数比较值之差
}
INFO A[MAX_N]; int size_A = 0;
int main() {
int N, L, H; //人数,及格下限,优良下限
scanf("%d %d %d", &N, &L, &H);
for (int i = 0; i < N; i++) {
U32 ID; int virtue, talent;
//★注意不能直接 scanf("%d %d", &A[size_A].virtue, &A[size_A].talent);
//在OJ平台Linux系统下:用scanf("%d",...)对结构体中的8位整型赋数值可能会覆盖邻近的成员导致错误!
scanf("%u %d %d", &ID, &virtue, &talent);
if (virtue < L || talent < L)continue; //排除不合格者①
A[size_A].ID = ID;
A[size_A].virtue = virtue;
A[size_A].talent = talent;
A[size_A].total = virtue + talent;
if (virtue >= H) { //德优
A[size_A].mark = (talent >= H) + 2; //当(talent >= H): 为真表示圣人 mark=3;为假表示君子 mark=2
}else { //L ≤ virtue < H ,L ≤ talent
A[size_A].mark = (virtue >= talent);//当(virtue >= talent):为真表示愚人mark=1;为假表示小人mark=0
}
++size_A; //注意 size_A 不一定与i同步,因为有排除不合格者。
}
qsort(A, size_A, sizeof(INFO), cmp);
printf("%d\n", size_A);
for (int i = 0; i < size_A; ++i) {
printf("%08u %d %d\n", A[i].ID, A[i].virtue, A[i].talent);
} //ID为 8 位整数
system("pause");
return 0;
}
可能大家会觉得C语言的cmp函数写得一点儿也不优雅,这是因为qsort使用的比较函数参数是void指针,你必须自己设计用什么类型,占多少字节,如何比较。所以用C语言写这个必须理解计算机的数据储存原理,C语言本来就是面向过程语言。