团体程序设计天梯赛题目集 L3-29 还原文件

题面描述:

一份重要文件被撕成两半,其中一半还被送进了碎纸机。我们将碎纸机里找到的纸条进行编号,如图 1 所示。然后根据断口的折线形状跟没有切碎的半张纸进行匹配,最后还原成图 2 的样子。要求你输出还原后纸条的正确拼接顺序。

团体程序设计天梯赛题目集 L3-29 还原文件_第1张图片

图1 纸条编号

团体程序设计天梯赛题目集 L3-29 还原文件_第2张图片

图2 还原结果

输入格式:

输入首先在第一行中给出一个正整数 N(1

随后一行给出一个正整数 M(≤100),为碎纸机里的纸条数量。接下去有 M 行,其中第 i 行给出编号为 i(1≤i≤M)的纸条的断口信息,格式为:

K h[1] h[2] ... h[K]

其中 K 是断口折线角点的个数(不超过 10^4+1),后面是从左到右 K 个折线角点的高度值。为简单起见,这个“高度”跟没有切碎的半张纸上断口折线角点的高度是一致的。

输出格式:

在一行中输出还原后纸条的正确拼接顺序。纸条编号间以一个空格分隔,行首尾不得有多余空格。

题目数据保证存在唯一解。

输入样例:

17
95 70 80 97 97 68 58 58 80 72 88 81 81 68 68 60 80
6
4 68 58 58 80
3 81 68 68
3 95 70 80
3 68 60 80
5 80 72 88 81 81
4 80 97 97 68

输出样例:

3 6 1 5 2 4

解题思路:

数据量并不算大,并且题目明说了解是唯一的,那么肯定不存在两张碎纸片的所有折线角点高度值都一样的情况,所以我们可以使用暴力一点的方法。可以将没有被切碎的半张纸当作一整行字符串输入进来存储到字符串变量str中,碎纸片的角点高度值也当作字符串进行存储,开辟一个结构体数组,存放每张碎纸片的角点高度值、编号、在str上的下标位置。使用string的成员函数find,即可找到碎纸片在str上的下标位置,然后我们把碎纸片根据find出来的位置从低到高进行排序即可,但是这样只能拿到26分,会有一个数据点WA掉。

代码:

#include 
using namespace std;
const int maxn = 1e5 + 10;
struct node
{
    string s;  // 碎纸片的每个顶点的高度
    int num;  // 碎纸片的编号
    int pos;    // 碎纸片在半张字条上应该在的位置
    node() {}
    node(string s, int num, int pos)
    {
        this->s = s;
        this->num = num;
        this->pos = pos;
    }
};
int n, m;
node a[maxn];

bool cmp(node &a, node &b)
{
    return a.pos < b.pos;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    char c;
    string str;
    cin >> c;
    // 将半张字条当作整行字符串输入
    getline(cin, str);
    cin >> m;
    // 输入每张碎纸片
    for (int i = 1; i <= m; i++)
    {
        int k;
        cin >> k;
        cin >> c;
        string s;
        getline(cin, s);
        // 找到这个碎纸片在半张字条当中的位置
        int pos = str.find(s);
        a[i] = node(s, i, pos);
    }
    // 将碎纸片按照在字条当中find出来的下标位置进行排序
    sort(a + 1, a + 1 + m, cmp);
    // 输出答案
    for (int i = 1; i <= m; i++)
    {
        if(i != 1)
            cout << " ";
        cout << a[i].num;
    }
    return 0;
}

团体程序设计天梯赛题目集 L3-29 还原文件_第3张图片

 可以试着找出能让程序WA的用例,比如两张碎纸片的角点高度值分别为1 2 3和1 2,那么此时我们使用find找出1 2在半张纸条当中的位置,然后再找出1 2 3的位置,就会出现错误。

解决办法:我们可以先处理1 2 3,先find出1 2 3的位置,然后将str中的1 2 3改成1*2 3,这样在后边处理1 2的时候就不会受影响了,所以我们在处理所有碎纸片之前先对存储碎纸片信息的结构体数组按照字典序降序进行排序。

具体代码如下:

#include 
using namespace std;
const int maxn = 1e5 + 10;
struct node
{
    string s;  // 碎纸片的每个顶点的高度
    int num;  // 碎纸片的编号
    int pos;    // 碎纸片在半张字条上应该在的位置
    node() {}
    node(string s, int num)
    {
        this->s = s;
        this->num = num;
    }
};
int n, m;
node a[maxn];

bool cmp(node &a, node &b)
{
    return a.pos < b.pos;
}

bool cmp1(node &a, node &b)
{
    return a.s > b.s;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    char c;
    string str;
    cin >> c;
    // 将半张字条当作整行字符串输入
    getline(cin, str);
    cin >> m;
    // 输入每张碎纸片
    for (int i = 1; i <= m; i++)
    {
        int k;
        cin >> k;
        cin >> c;
        string s;
        getline(cin, s);
        // 找到这个碎纸片在半张字条当中的位置
        a[i] = node(s, i);
    }
    // 要注意1 2 3、1 2这种特例,我们可以先处理1 2 3,然后把1 2 3改成1*2 3,这样再处理1 2的时候就不受影响了
    sort(a + 1, a + 1 + m, cmp1);
    for (int i = 1; i <= m; i++)
    {
        int pos = str.find(a[i].s);
        a[i].pos = pos;
        for (int j = pos; ; j++)
        {
            if(str[j] == ' ')
            {
                str[j] = '*';
                break;
            }
        }
    }
    // 将碎纸片按照在字条当中find出来的下标位置进行排序
    sort(a + 1, a + 1 + m, cmp);
    // 输出答案
    for (int i = 1; i <= m; i++)
    {
        if(i != 1)
            cout << " ";
        cout << a[i].num;
    }
    return 0;
}

团体程序设计天梯赛题目集 L3-29 还原文件_第4张图片

 

显然这不是唯一的解法,更不是最优的解法,其他解法还有dfs、字符串hash。先占个坑,后续补上。

你可能感兴趣的:(算法&数据结构,算法竞赛,算法)