# NENUOJ 之 算法2排序B

# 前言

不太好写的一章节~愿读者充满耐心地学习每一道题,如果是面对小测,可以提前准备一下,因为这一章不太容易在初次见到的时候马上做出来。

# B001 快乐的蠕虫 (opens new window)

# 题目描述

有一只快乐的蠕虫居住在一个m×n大小的网格中。在网格的某些位置放置了k块石头。网格中的每个位置要么是空的,要么放置了一块石头。当蠕虫睡觉时,它在水平方向或垂直方向上躺着,把身体尽可能伸展开来。蠕虫的身躯既不能进入到放有石块的方格中,也不能伸出网格外。而且蠕虫的长度不会短于2个方格的大小。 本题的任务是给定网格,要计算蠕虫可以在多少个不同的位置躺下睡觉。

# 输入

输入文件的第1行是一个整数t,1<=t<=11,表示测试数据的个数。每个测试数据的第1行为3个整数:m,n和k(0<=m,n,k<=200000),接下来有k行,每行为2个整数,描述了一块石头的位置(行和列,最左上角位置为(1,1))。

# 输出

对每个测试数据,输出占一行,为一个整数,表示蠕虫可以躺着睡觉的不同位置的数目。

# 样例输入 复制

1
5 5 6
1 5
2 3
2 4
4 2 
4 3
5 1

# 样例输出 复制

9

# 思路:

笔者觉得这题挺难写的,基本思路就是把网格四周一圈也当作石头,然后在内部通过x轴排序还有y轴排序的方式,分别去放蠕虫,然后计数,虽然很不喜欢这道题,但是好像考试考的概率特别大【悲】愿读者自求多福。

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
typedef struct stone{
    int x;
    int y;
}s;
s st[200007];
bool cmp1(s a,s b){
    if(a.y == b.y) return a.x < b.x;
    return a.y < b.y;
}
bool cmp2(s a,s b){
    if(a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}
void solve(){
    int m,n,k;
    cin >> m >> n >> k;
    int cnt = 0;
    for(int i = 0;i <= n + 1;i++){
        st[cnt].x = 0,st[cnt].y = i;
        cnt++;
        st[cnt].x = m + 1,st[cnt].y = i;
        cnt++;
    }
    for(int i = 0;i <= m + 1;i++){
        st[cnt].x = i,st[cnt].y = 0;
        cnt++;
        st[cnt].x = i,st[cnt].y = n + 1;
        cnt++;
    }
    for(int i = 1;i <= k;i++){
        int x,y;
        cin >> x >> y;
        st[cnt].x = x,st[cnt].y = y;
        cnt++;
    }
    sort(st,st + cnt,cmp1);
    int ans = 0;
    for(int j = 0;j < cnt - 1;j++){
        if(st[j + 1].y == st[j].y && abs(st[j + 1].x - st[j].x) > 2){
            ans++;
        }
    }
    sort(st,st + cnt,cmp2);
 
    for(int j = 0;j < cnt - 1;j++){
        if(st[j + 1].x == st[j].x && abs(st[j + 1].y - st[j].y) > 2){
            ans++;
        }
    }
    cout << ans << "\n";
}
int main(){ 
    int t;
    cin >> t;
    while(t--) solve();
}

# B002 单词重组 (opens new window)

# 题目描述

在美国数以百万计的报纸中,有一种单词游戏称为猜词。游戏的目标是猜谜,为了找出答案中缺少的字母,有必要对4个单词的字母顺序重新调整。在本题中,你的任务是编写程序实现对单词中的字母顺序重新调整。

# 输入

输入文件包含4部分: (1) 一部字典,包含至少1个单词,至多100个单词,每个单词占一行;

(2) 字典后是一行字符串“XXXXXX”,表示字典结束;

(3) 一个或多个被打乱字母顺序的“单词”,每个单词占一行,你必须整理这些字母的顺序;

(4) 输入文件的最后一样为字符串“XXXXXX”,代表输入文件结束。

所有单词,包括字典中的单词和被打乱字母顺序的单词,都只包含小写英文字母,并且至少包含一个字母,至多包含6个字母。字典中的单词不一定是按顺序排列的,但保证字典中的单词是唯一的。

# 输出

对输入文件中每个被打乱字母顺序的单词w,按字母顺序输出字典中所有满足以下条件的单词的列表:通过调整单词w中的字母顺序,可以变成字典中的单词。列表中的每个单词占一行。如果列表为空(即单词w不能转换成字典中的任何一个单词),则输出一行字符串“NOT A VALID WORD”。以上两种情形都在列表后,输出一行包含6个星号字符的字符串,表示列表结束。

# 样例输入 复制

tarp
given
score
refund
only
trap
work
earn
course
pepper
part
XXXXXX
resco
nfudre
aptr
sett
oresuc
XXXXXX

# 样例输出 复制

score
******
refund
******
part
tarp
trap
******
NOT A VALID WORD
******
course
******

# 思路:

这道题笔者暂时只能想到这种做法,如果有更好的可以分享~

笔者这里是用map来实现的,就是给每个单词对应了一个排序后的映射,比如这里会认为abcd和dcba会有相同的映射即abcd。然后后续输出单词的时候,逐一输出map里相同映射的那些单词~

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
map<string,string> mp;
int main(){
    string s;
    while(cin >> s){
        if(s == "XXXXXX") break;
        string str = s;
        sort(s.begin(),s.end());
        mp[str] = s;
    }
    while(cin >> s){
        if(s == "XXXXXX") break;
        int cnt = 0;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            // cout << it->first << " => " << it->second << '\n';
            sort(s.begin(),s.end());
            if(it -> second == s){
                cnt++;
                cout << it -> first << "\n";
            }
        }
        if(cnt == 0){
            cout << "NOT A VALID WORD\n******\n";
        }else{
            cout << "******\n";
        }
    }
}

# B003 英文姓名排序 (opens new window)

# 题目描述

在汉语里,对汉语姓名可以按拼音排序,也可以按笔画顺序排序。在英语里,对英语姓名主要按字母顺序排序。本题要求给定的一组英文姓名按长短顺序排序。

# 输入

输入文件中包含多个测试数据。每个测试数据的第1行是一个正整数N(0 < N < 100),表示该测试数据中英文姓名的数目;接下来有N行,每行为一个英文姓名,姓名中允许出现的字符有大小写英文字母、空格和点号(.),每个英文姓名长度至少为2、但不超过50.N=0表示输入结束。

# 输出

对输入文件中的每个测试数据,输出排序后的姓名。排序方法为:先按姓名长度从长到短的顺序排序,对长度相同的姓名,则按字母顺序排序。每2个测试数据的输出之间输出一个空行。

# 样例输入 复制

4
David A. Forsyth
Jean Ponce
Tom M. Mitchell
Thomas H. Cormen
0

# 样例输出 复制

David A. Forsyth
Thomas H. Cormen
Tom M. Mitchell
Jean Ponce

# 思路:

这道题思路还是很简单的,就是排序,然后处理一下排序逻辑,但是因为阴间的一些换行符问题,这里不能用getchar(),所以用了getline去吞换行符。

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
string str[1000];
bool cmp(string a,string b){
    if(a.size() != b.size()) return a.size() > b.size();
    return a < b;
}
int main(){
    int n;
    while(cin >> n){
        if(!n) break;
        string s;
        getline(cin,s);
        for(int i = 1;i <= n;i++){
            getline(cin,s);
            str[i] = s;
        }
        sort(str + 1,str + n + 1,cmp);
        for(int i = 1;i <= n;i++){
            cout << str[i] << "\n";
        }
        cout << "\n";
    }
}

# B004 DNA排序 (opens new window)

# 题目描述

一个序列的逆序数定义为序列中无序元素对的数目。例如,在字符序列DAABEC中,逆序数为5,因为字符D比它右边的4个字符大,而字符E比它右边的1个字符大。字符序列AACEDGG只有1个逆序,即E和D,它几乎是已经排好序的,而字符序列“ZWQM”有6个逆序,它是最大程度的无序,即有序序列的逆序。 在本题中,你的任务是对DNA字符串(只包含字符“A”、“C”,“G”和“T”)进行排序。注意不是按照字母顺序,而是按照逆序数从低到高进行排序,所有字符串的长度都一样。

# 输入

输入文件中包含多组测试数据。每组测试数据的格式为:第1行为2个整数,正整数n(0 < n <= 50,表示字符串的长度)和正整数m(1 < m <= 100,表示字符串的数目);然后是m行,每一行为一个字符串,长度为n。

# 输出

对应到输入文件中的N组测试数据,输出也有N组,每2组输出之间有一个空行。对每组输入数据,按逆序数从低到高输出各字符串,如果2个字符串的逆序数一行,则按输入时的先后顺序输出。

# 样例输入 复制

10 5
TTTTGGCCAA
TTTGGCCAAA
GATCAGATTT
CCCGGGGGGA
ATCGATGCAT

# 样例输出 复制

CCCGGGGGGA
GATCAGATTT
ATCGATGCAT
TTTTGGCCAA
TTTGGCCAAA

# 思路:

笔者最开始还想着分两个数组进行处理,但是后面发现排序很难同步处理,干脆就换成结构体排序的写法了。

# 代码C++:

#include<bits/stdc++.h>
using namespace std;

struct node{
    string s;
    int cnt = 0;
}node[1000];
bool cmp(struct node a,struct node b){
    return a.cnt < b.cnt;
}
int main(){
    int n,m;
    while(cin >> n >> m){
        for(int i = 1;i <= m;i++){
            string s;
            cin >> s;
            node[i].s = s;
            int cnt = 0;
            for(int j = 0;j < n;j++){
                for(int k = j + 1;k < n;k++){
                    if(s[j] > s[k]) cnt++;
                }
            }
            node[i].cnt = cnt;
        }
        sort(node + 1,node + m + 1,cmp);
        for(int i = 1;i <= m;i++){
            cout << node[i].s << "\n";
        }
        cout << "\n";
    }
    
}

# B005 体重排序 (opens new window)

# 题目描述

作为一台很受欢迎的脱口秀节目的主持人,你正在做一期关于节食的节目。你的嘉宾是Kevorkian博士。他最近推出了一项减肥计划“Do You Want To Diet?”,这项计划向它的用户保证每天减肥1磅。 节目录制那天,你准备让一些使用Kevorkian博士减肥计划的节食者上台秀一下。你准备按他们的体重的递减顺序来安排他们出场的先后顺序。问题是他们报名时只提供了以下信息:姓名,节食的天数,节食前的体重。你要根据他们节食的天数来计算他们现在的体重。所有的节食者每天减肥1磅。

# 输入

输入文件包含至多100个测试数据。测试数据之间没有空行。每个测试数据包含3部分: 第1行为START; 接下来为节食者列表:包含1~10行,每行描述一名节食者,包括姓名、节食的天数和节食前的体重。其中姓名为1~20个数字、字母字符组成的字符串;节食的天数不超过1000天;节食前的体重不超过10,000。 最后一行为END。

# 输出

对每个测试数据,根据各节食者现在体重的递减顺序列出节食者的名字,每个节食者的名字占一行。每2个测试数据的输出之间有一个空行。

# 样例输入 复制

START
Joe 10 110
END
START
James 100 150
Laura 100 140
Hershey 100 130
END

# 样例输出 复制

Joe

James
Laura
Hershey

# 思路:

这题主要是处理START和END有点麻烦,可以参考笔者这里的做法~

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
struct node{
    string name;
    int day = 0;
    int w = 0;
}node[10000];
bool cmp(struct node a,struct node b){
    return a.w - a.day > b.w - b.day;
}
int main(){
    string start;
    string end;
    while(cin >> start){
        int cnt = 0;
        string name;
        int day = 0;
        int w = 0;
        while(cin >> name){
            if(name == "END") break;
            cin >> day >> w;
            cnt++;
            node[cnt].name = name;
            node[cnt].day = day;
            node[cnt].w = w;
        }
        sort(node + 1,node + cnt + 1,cmp);
        for(int i = 1;i <= cnt;i++){
            cout << node[i].name << "\n";
        }
        cout << "\n";
    }
}

# B006 俄罗斯套娃 (opens new window)

# 题目描述

俄罗斯套娃大家应该都玩过。是一个按照大小顺序可以嵌套在一起的玩具。现在有一个被拆开的俄罗斯套娃摆到了你的好友面前,但是,要想把它重新变成一个娃娃,必须要满足这样的规则: 1.娃娃的大小必须是从小到大排列好的。 2.你每次只可以交换相邻的两个娃娃。 这样的规则使你的好友变得很烦躁,假设娃娃的个数为n,如果交换娃娃的次数超过n*(n - 1) / 2 - 1次,那么你的好友就会烧掉这些娃娃。但是她很珍惜这些娃娃。现在她向你询问,她是否不会烧掉这个俄罗斯套娃?

# 输入

一个整数n(n <= 1000) 接下来n个数Si,Si表示当前位置娃娃大小。(Si不一定小于n)。

# 输出

如果好友不会烧掉娃娃输出"YES"(没有引号),反之输出"NO"(没有引号)。

# 样例输入 复制

6
6 5 4 3 2 1

# 样例输出 复制

NO

# 思路:

笔者最开始想得乱七八糟的,结果暴力排序就可以了hhh

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
int s[1000];
int main(){
    int n;
    cin >> n;
    for(int i = 0;i < n;i++){
        cin >> s[i];
    }
    int cnt = 0;
    for(int i = 0;i < n - 1;i++){
        for(int j = 0;j < n - 1- i;j++){
            if(s[j] > s[j + 1]){
                int temp = s[j];
                s[j] = s[j + 1];
                s[j + 1] = temp;
                cnt++;
            }
        }
    }
    cout << ((cnt > n * (n - 1) / 2 - 1) ? "NO\n" : "YES\n");
}

# B007 组合数字 (opens new window)

# 题目描述

现在给你n个正整数,a1,a2,a3........an,请你把它们首尾连接到一起,组成一个最小的正整数(可能会超出long long)。

# 输入

第一行有一个整数,表示数字个数 n。 第二行有 n 个整数,表示给出的 n 个整数 ai(1 <= n <= 20,1 <= ai <= 1e9)。

# 输出

一个正整数,表示最小的整数。

# 样例输入 复制

3
13 312 343

# 样例输出 复制

13312343

# 思路:

很经典的字符串拼接比较的方法就是

bool cmp(string a,string b){
    return a + b < b + a;
}

一定要理解哦~

# 代码C++:

#include<bits/stdc++.h>
using namespace std;
string a[1000];
bool cmp(string a,string b){
    return a + b < b + a;
}
int main(){
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++){
        cin >> a[i];
    }
    sort(a + 1,a + n + 1,cmp);
    for(int i = 1;i <= n;i++){
        cout << a[i];
    }
}