tzyt
2021-09-06 07:04:45
update:2022/4/17 更正了博客地址
题目链接
博客中观看体验更好
前言:这篇题解写的可能比较啰嗦,主要时是因为我把所有思考的过程都写下来了,所以如果你已经有了基本的思路,或者是希望找一篇简洁的题解,就可以跳过这篇题解了。
总共有
单看这样的描述可能有些抽象,现在我们来看一下样例是怎么样的,再在样例的基础上思考应该怎么解决这道题。
在样例中
"large" | "small" | N/A | |
"brown" | "white" | "spotted" | |
"noisy" | "silent" | N/A |
因为样例中让我们求的是按照字典序排在第7的牛,我们可以思考一下以字典序为关键字进行排序的过程是什么样的。
举个例子:有两个字符串"abc"和字符串"cde"需要进行字典序排序,首先我们应该比较在第一位的字符"a"和"c"的字典序,再比较第二位"b"和"c"的字典序,最后才是第三位的字符。
从这个过程中我们可以发现每一位字符对于字符串整体字典序的影响是不一样的,其中第一位的影响最大,最后一位最小。因此我们就可以说他们对于整个字符串的字典序的影响的“权值”不同。如果我们按照字典序给不同的字符串从小到大排序,对于一个字符串,不管从第二个字符到最后一个字符的字典序有多小,如果第一个字符的字典序很大,那么它也会排在很后面
再观察这道题目中让我们求解的问题,我们可以发现,第1类形容词比如"large"对于整串字符的影响是最大的,其次是第二类,比如"brown",最后才是第三类。
分析到这里,相信你已经体会到这个问题和数字系统的相关性了。
那就是我们在比较数字时,采用的方法也是从高位到低位进行比较
比如有这样一个
数字
数字
数字
把整个规则推广到
那么问题就来了,不管是我们刚刚讨论的
我们可以先尝试解决每类形容词数量一定的情况,设每类形容词的数量都是 rank[i][j]
中,其中
这一步的目的是把字符串转化成数字,方便后续的计算。(把每个形容词映射到数字系统中的 “第
因为我们已经完成了形容词到数字的映射,所以下面要做的就等于“把
这样的例子可能有些抽象,下面我们来模拟一遍这个过程
我们规定第一类形容词有以下两个 {"a","b"}, 第二类形容词也有以下两个{"c","d"}, 第三类是{"e","f"}。
那么我们可以求出以下的rank
数组
(因为方便计算,所以排名从0开始)
"a" | "b" | |
"c" | "d" | |
"e" | "f" |
如果我们想求字典序排在第
在解决刚刚简化过的问题的过程中,我们把每类形容词的类映射到了数字系统的 “第几位数”,把它们的排名
我们可以发现,解决原问题的关键就在于进制,在刚刚简化过的问题中,数字系统中每一位的进制是一样的,并且每一类形容词的数量也是一定的,那么再解决当前问题时,每一类的形容词数量是不一定的,所以相应的,每一位的进制也要有所改变。
回到题目给的样例,每类形容词的数量是
而第三类的形容词被我们映射到了数字系统中的第一位数字,第二类是第二位,同样的,第一类就是第三位
因此我们可以规定,在第一位的时候,这个数字系统是二进制的,在第二位的时候,这个数字系统是三进制的,第三位也是二进制。
虽然我们可以做到用这样的 "
大家肯定对十进制转二进制这样的问题非常熟悉,比如要把一个十进制数
写成程序的话,就是这样的
//k是十进制数
//weight_in_pos[i]是第i位(从最高位开始计,和我们平常用的方法相反)在十进制中代表的值
//i是当前位(从最高位开始计,和我们平常用的方法相反)
for (int i = 1; i <= adj_num; i++)
{
cout << adj_by_pos[i][(k) / weight_in_pos[i]] << " ";
k %= weight_in_pos[i];
}
所以呢,对于 "
(每一位在十进制中代表的值的意思就是 在原进制系统中,如果这一位是
那么这样的进制的每一位在十进制中代表的值如何计算呢?
不管是在几进制的系统里,只要两个数的进制系统相同,那么一个 位数更多的数 所代表的值一定比位数更少的那个要大。
所以我们可以这样计算第
有了这个结论就可以递推的求解每一位在十进制中代表的值,我们可以把答案存在 weight_in_pos[i]
数组中(第
在样例的 "
至此,我们已经能计算出所有牛中排在第
这个小问题的求解就比较简单了,我们可以把
细节都有注释
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n, k;
int adj_num = 0;
vector<string> str[105]; //str[i]表示用于修饰第i头牛的形容词
vector<string> adj_by_pos[35]; //adj_by_pos[i]表示所有在位置i出现的形容词
set<string> is_appear[35]; //is_appear[i]用于判断在位置i上,某个形容词是否出现
int weight_in_pos[35]; //每一位代表的值(按照字典序排在第几)
map<string, int> rank_in_pos[35]; //rank_in_pos[i][j]代表在位置i上,字符串j按照字典序排序,排在第几
int cow_rank[105]; //fj没有的牛的排名
bool debug = false; //调试开关,可以打开去体会一下解题的过程
void mapping() //对每类形容词进行字典序排序,把结果存在 rank[i][j]中,其中 i 代表形容词类型,j 代表排名
{ //注意这里的排名从0开始(这是把单词映射到数字上,数字是从0开始的)
for (int i = 1; i <= adj_num; i++)
{
int rank = 0;
for (auto j : adj_by_pos[i]) //c++11的新特性,意思是用j遍历所有adj_by_pos[i]的元素
{
rank_in_pos[i][j] = rank;
if (debug)
cout << j << " rank = " << rank << " i = " << i << endl;
rank++;
}
}
}
int get_pos(int cow_id)
{
int ans = 0;
for (int i = 1; i <= adj_num; i++)
{
ans += weight_in_pos[i] * (rank_in_pos[i][str[cow_id][i - 1]]);
}
return ans + 1; //答案可能是0,但是排位应该从1开始
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> k;
string temp_str;
for (int i = 1; i <= n; i++)
{
cin >> temp_str;
while (temp_str != "no")
{
cin >> temp_str;
}
int adj_pos = 1;
while (1)
{
cin >> temp_str;
if (temp_str == "cow.")
{
break;
}
str[i].push_back(temp_str);
if (!is_appear[adj_pos].count(temp_str)) //还没有出现过,这里是去重操作
{
adj_by_pos[adj_pos].push_back(temp_str);
is_appear[adj_pos].insert(temp_str);
}
if (i == 1)
{
adj_num++; //计算形容词的种类数
}
adj_pos++;
}
}
for (int i = 1; i <= adj_num; i++)
{
sort(adj_by_pos[i].begin(), adj_by_pos[i].end()); //对每个类型的形容词进行排序
}
weight_in_pos[adj_num + 1] = 1; //第一位代表的数字应该是1
adj_by_pos[adj_num + 1].push_back("temp"); //1和1相乘是1,所以要push一个元素进去,这样子size就是1
for (int i = adj_num; i >= 1; i--) //计算每一位在十进制中代表的值(这里的位的计算方法和平时的方法是反的)
{ //这是因为本题中的形容词类型和数字系统的 “位” 是相反的
if (debug)
cout << "i" << i << endl;
weight_in_pos[i] = weight_in_pos[i + 1] * adj_by_pos[i + 1].size();
}
mapping();
for (int i = 1; i <= n; i++)
{
cow_rank[i] = get_pos(i); //计算出要删除的n头牛的整体排名
if (debug)
cout << "cowrkw " << i << " = " << cow_rank[i] << endl;
}
sort(cow_rank + 1, cow_rank + n + 1); //按照每种牛的排名进行排序
for (int i = 1; i <= n; i++)
{
if (cow_rank[i] <= k)
{
k++;
}
else
{
break;
}
}
k--; //k原本代表的是排在第几的牛,但是我们已经把牛的排名转化成数字了,
//排名最小的牛的数字是0而不是1,所以这里要减1
if (debug)
cout << "new k" << k << endl;
for (int i = 1; i <= adj_num; i++)
{
cout << adj_by_pos[i][(k) / weight_in_pos[i]] << " ";
if (debug)
cout << "i " << i << " (k) / weight_in_pos[i] " << (k) / weight_in_pos[i] << endl;
k %= weight_in_pos[i];
}
system("pause");
}
题解就到这里了,如果你发现题解有问题,或是有看不懂的地方,都欢迎私信我或者是在评论区里讲,如果你觉得对你有帮助就点个赞吧,谢谢。