PTA数据结构与算法实习题目集3 学习笔记分享

9 重名剔除(Deduplicate)

题目描述:

Epicure先生正在编撰一本美食百科全书。为此,他已从众多的同好者那里搜集到了一份冗长的美食提名清单。既然源自多人之手,其中自然不乏重复的提名,故必须予以筛除。Epicure先生因此登门求助,并认定此事对你而言不过是“一碟小菜”,相信你不会错过在美食界扬名立万的这一良机。

输入格式:

第1行为1个整数n,表示提名清单的长度。以下n行各为一项提名。

要求:
1 < n < 6 * 10^5
提名均由小写字母组成,不含其它字符,且每项长度不超过40字符。

输出格式:

所有出现重复的提名(多次重复的仅输出一次),且以其在原清单中首次出现重复(即第二次出现)的位置为序。

输入样例:

10
brioche
camembert
cappelletti
savarin
cheddar
cappelletti
tortellni
croissant
brioche
mapotoufu

输出样例:

cappelletti
brioche

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

参考代码:

#include<bits/stdc++.h>
//载入万能头
#define MAXSIZE 600000+10

using namespace std;

char name[MAXSIZE][40+1];//提名的名字字符数组


//定义一个菜单的结构体
struct menu
{
    char* my_data;//初始数据,使用字符的数组指针,可以更改值还能整体或者单个访问
    bool flag;//flag用以记录前面是否有出现过,false代表未出现过,true标记前面出现过
    menu* str_tm;//每个数据项的菜单指针

}my_data[MAXSIZE];//把menu定义的数据命名为my_data,方便使用

//写入输入的提名
void Write(int a, char* s)
{
    menu* x = new menu;//创建一个新的菜单
    x->my_data = s;//新菜单的值=键盘输入的字符
    x->flag = false;//标识初始化为false,即之前未出现重名
    x->str_tm = my_data[a].str_tm;//输入的字符指针传给新建菜单的指针
    my_data[a].str_tm = x;//完成题目数据的写入(存入)
}

//哈希函数,传入参数s—字符数组指针s —>字符关键字生成
int my_hash(char*s)
{
    int sum = 0;
    int slen = strlen(s);
    for(int i = 0; i < slen; i++)
    {
        sum += (i + 1)*(s[i] - 'a' + 1);//生成独一无二的hash值
    }
    return sum;//返回hash值
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%s", name[i]);//一行一行输入
        int a = my_hash(name[i]) % MAXSIZE;//除法取余获得hash地址码
        menu* p = my_data[a].str_tm;//将hash地址码存给菜单指针p
        while (p)// 非零时
            if (!strcmp(p->my_data, name[i]))//查询到所存的地址和字符存在了的
            {
                if (!p->flag)//第二次(或者多次)出现的
                {
                    p->flag = true;//标识改为true,只允许输出一次
                    printf("%s\n",name[i]);//输出第一次重复出现的
                }
                break;
            }
            else
            {
                p = p->str_tm;//此前并未出现过的,分配新地址
            }
        if (!p)//分配有新地址
        {
            Write(a, name[i]);//写入
        }
    }
    return 0;
}





//本题的逻辑结构:查找-Hash散列
//本题的存储结构:链式储存,链式存储处理冲突

//用flag标记字符,false标记允许打印(未出现过或者只出现了一次),true标记打印过了(不用再打印)

/*
1、获取字符串(关键码),计算散列码,使用散列码计算哈希值。
2、尝试放入槽位。如果有冲突&&槽位关键码与当前串相同&&该关键码也未打印过,转3。
   如果有冲突&&关键码相同&&打印过,转1。
   如果有冲突&&关键码不同,转4。
   如果没有冲突,转5。
3、打印当前字符串,标记槽位为已打印。转1。
4、进行除法取余,转2。
5、字符串放入槽位,标记为未打印,转1
*/

//效率:
//时间复杂度:O(n²)
//空间复杂度:S(n) = O(n²)


//测试数据:(1)输入:
/*
10
brioche
camembert
cappelletti
savarin
cheddar
cappelletti
tortellni
croissant
brioche
mapotoufu
*/

//          (2)输出:
/*
cappelletti
brioche
*/





10 玩具(Toy)

题目描述:

ZC神最擅长逻辑推理,一日,他给大家讲述起自己儿时的数字玩具。
该玩具酷似魔方,又不是魔方。具体来说,它不是一个3 * 3 * 3的结构,而是4 * 2的结构。

按照该玩具约定的玩法,我们可反复地以如下三种方式对其做变换:
A. 交换上下两行。比如,图(a)经此变换后结果如图(b)所示。
B. 循环右移(ZC神从小就懂得这是什么意思的)。比如,图(b)经此变换后结果如图(c)所示。
C. 中心顺时针旋转。比如,图(c)经此变换后结果如图(d)所示。
ZC神自小就是这方面的天才,他往往是一只手还没揩干鼻涕,另一只手已经迅速地将处于任意状态的玩具复原至如图(a)所示的初始状态。物质极其匮乏的当年,ZC神只有一个这样的玩具;物质极大丰富的今天,你已拥有多个处于不同状态的玩具。现在,就请将它们全部复原吧。

输入格式:

第一行是一个正整数,即你拥有的魔方玩具总数N。
接下来共N行,每行8个正整数,是1~8的排列,表示该玩具的当前状态。
这里,魔方状态的表示规则为:前四个数自左向右给出魔方的第一行,后四个数自右向左给出第二行。比如,初始状态表示为“1 2 3 4 5 6 7 8”。

要求:
1 <= N <= 1,000

输出格式:

共N行,各含一个整数,依次对应于复原各玩具所需执行变换的最少次数。
特别地,若某个玩具不可复原,则相应行输出-1。

输入样例:

2
1 2 3 4 5 6 7 8
8 6 3 5 4 2 7 1

输出样例:

0
2

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

参考代码:

#include<bits/stdc++.h>
//载入万能头
#define MAX (8*7*6*5*4*3*2)+1

using namespace std;


int factorial[8] = {1, 1, 2, 6, 24, 120, 720, 5040};//数位上的阶乘

//定义一种状态信息
struct Information
{
    bool flag = false;
    int A[8];//放每行的位数
    int step = -1;//标识BFS搜索的次数,-1代表还未遍历过这里
};

Information my_data[MAX];//存初始数据

struct My_list//定义我们的队列操作
{
    int my_data[MAX];//用数组来存
    int front = 0, end = 0;//头、 尾
    int pop()//进
    {
        int temp = front;
        front = (front + 1) % MAX;
        return my_data[temp];
    }
    void push(int i)//出
    {
        my_data[end] = i;
        end = (end + 1) % MAX;
        return;
    }
    bool empty()
    {
        return front == end;
    }
};

template <typename T>//使用类模板,减轻工作量
void print(T A[], int n)//改写print输出函数
{
    for (int i = 0; i != n; ++i)
    {
        cout << A[i] << ' ';
    }
    putchar('\n');
    return;
}

void Check(int w, int v, My_list &Q)
{
    if (my_data[w].step == -1)
    {
        my_data[w].step = my_data[v].step + 1;
        Q.push(w);
    }
    return;
}

int contor(int A[], int n)
{
    int res = 0;
    for (int i = 0; i != n; ++i)
    {
        int smaller = 0;
        for (int j = i + 1; j != n; ++j)
        {
            if (A[j] < A[i])
            {
                ++smaller;
            }
        }
        res += smaller * factorial[n-i-1];
    }
    return res;
}

void re_contor(int stateNo, int n)
{
    int temp;
    bool used[n];
    memset(used, false, sizeof(used));
    int tempStateNo = stateNo;
    for (int i = 0; i != n - 1; ++i)
    {
        temp = tempStateNo / factorial[n-i-1];
        int rank = 0;
        for (int j = 1; j != n + 1; ++j)
        {
            if (!used[j-1])
            {
                ++rank;
            }
            if (rank == temp + 1)
            {
                my_data[stateNo].A[i] = j;
                used[j-1] = true;
                break;
            }
        }
        tempStateNo %= factorial[n-i-1];
    }
    for (int i = 0; i != n; ++i)
    {
        if (!used[i])
        {
            my_data[stateNo].A[n-1] = i + 1;
            break;
        }
    }
    my_data[stateNo].flag = true;
    return;
}

void swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
    return;
}

// 上下互换
int operate_1(int A_o[])
{
    int A[8];
    memcpy(A, A_o, sizeof(A));
    swap(A[0], A[7]);
    swap(A[1], A[6]);
    swap(A[2], A[5]);
    swap(A[3], A[4]);
    int res = contor(A, 8);
    memcpy(my_data[res].A, A, sizeof(A));
    my_data[res].flag = true;
    return res;
}

// 循环左移
int operate_2(int A_o[])
{
    int A[8];
    memcpy(A, A_o, sizeof(A));
    int temp1 = A[0], temp2 = A[7];
    A[0] = A[1];
    A[1] = A[2];
    A[2] = A[3];
    A[7] = A[6];
    A[6] = A[5];
    A[5] = A[4];
    A[3] = temp1;
    A[4] = temp2;
    int res = contor(A, 8);
    memcpy(my_data[res].A, A, sizeof(A));
    my_data[res].flag = true;
    return res;
}

// 逆时针旋转
int operate_3(int A_o[])
{
    int A[8];
    memcpy(A, A_o, sizeof(A));
    int temp = A[1];
    A[1] = A[2];
    A[2] = A[5];
    A[5] = A[6];
    A[6] = temp;
    int res = contor(A, 8);
    memcpy(my_data[res].A, A, sizeof(A));
    my_data[res].flag = true;
    return res;
}

void BFS()
{
    My_list Q;
    Q.push(0);
    my_data[0].step = 0;
    while (!Q.empty())
    {
        int v = Q.pop();
        if (!my_data[v].flag) re_contor(v, 8);
        int w1 = operate_1(my_data[v].A), w2 = operate_2(my_data[v].A), w3 = operate_3(my_data[v].A);
        Check(w1, v, Q);
        Check(w2, v, Q);
        Check(w3, v, Q);
    }
    return;
}

int main(int argc, char const *argv[])
{
    BFS();
    int n;
    scanf("%d", &n);
    for (int i = 0; i != n; ++i)
    {
        int A[8];
        for (int j = 0; j != 8; ++j)
        {
            scanf("%d", &A[j]);
        }
        int stateNo = contor(A, 8);
        printf("%d\n", my_data[stateNo].step);
    }
    return 0;
}


//本题的逻辑结构:状态图-BFS搜索
//本题的存储结构:线性存储

/*
一共有8!=40320中状态,通过哈希表建立映射(康托展开)。
大体思路就是从原始状态开始通过三种操作的反向给出一切可以达到的状态,
通过BFS进行探索。
如果某个状态已经实现过则回溯,因为BFS第一遍到达该结点的步数就是最短路径
故需要记录每个状态的访问标记和需要达到的最小步数,假设步数为-1即为未访问过。
对于逆康托要保存康托的结果,大大提高效率

*/

//效率:
//时间复杂度:O(n²)
//空间复杂度:S(n) = O(1)


//测试数据:(1)输入:
/*
2
1 2 3 4 5 6 7 8
8 6 3 5 4 2 7 1
*/

//          (2)输出:
/*
0
2
*/





11 任务调度(Schedule)

题目描述:

某高性能计算集群(HPC cluster)采用的任务调度器与众不同。为简化起见,假定该集群不支持多任务同时执行,故同一时刻只有单个任务处于执行状态。初始状态下,每个任务都由称作优先级数的一个整数指定优先级,该数值越小优先级越高;若优先级数相等,则任务名ASCII字典顺序低者优先。此后,CPU等资源总是被优先级数最小的任务占用;每一任务计算完毕,再选取优先级数最小的下一任务。不过,这里的任务在计算结束后通常并不立即退出,而是将优先级数加倍(加倍计算所需的时间可以忽略)并继续参与调度;只有在优先级数不小于2^32时,才真正退出。
你的任务是,根据初始优先级设置,按照上述调度原则,预测一批计算任务的执行序列。

输入格式:

第一行为以空格分隔的两个整数n和m,n为初始时的任务总数,m为所预测的任务执行序列长度,每行末尾有一个换行符。
以下n行分别包含一个整数和一个由不超过8个小写字母和数字组成的字符串。前者为任务的初始优先级数,后者为任务名。数字和字符串之间以空格分隔。

要求:
0 ≤ n ≤ 4,000,000
0 ≤ m ≤ 2,000,000
0 < 每个任务的初始优先级 < 2^32
不会有重名的任务

输出格式:

最多m行,各含一个字符串。按执行次序分别给出执行序列中前m个任务的名称,若执行序列少于m,那么输出调度器的任务处理完毕前的所有任务即可。

输入样例:

3 3
1 hello
2 world
10 test

输出样例:

hello
hello
world

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

参考代码:

#include<bits/stdc++.h>
//载入万能头
#define MAXSIZE 4000000+10

using namespace std;


struct node
{
    long long int my_data1;
    char my_data2[9];
    friend bool operator<(node x, node y)
    {
        if (x.my_data1 != y.my_data1)
        {
            return x.my_data1 < y.my_data1;
        }
        return strcmp(x.my_data2, y.my_data2) < 0;
    }
} S[MAXSIZE];

int j = 0;

void up(int p)
{
    while (p > 1)
    {
        if (S[p] < S[p / 2])
        {
            swap(S[p], S[p / 2]);
            p /= 2;
        }
        else
        {
            break;
        }

    }
}

void down(int p)
{
    int temp = p * 2;
    while (temp <= j)
    {
        if (temp < j && S[temp + 1] < S[temp])
        {
            temp++;
        }
        if (S[temp] < S[p])
        {
            swap(S[p], S[temp]);
            p = temp;
            temp *= 2;
        }
        else
        {
            break;
        }
    }
}


void Ins(node my_data1)
{
    j += 1;
    S[j] = my_data1;
    up(j);
}


void Extract()
{

    S[1] = S[j];
    j -= 1;
    down(1);
}


int main()
{
    int n, m;
    node d;
    scanf("%d %d", &n, &m);
    long long int p = 1;
    for (int i = 0; i < 32; i++)
    {
        p *= 2;
    }

    for (int i = 0; i < n; i++)
    {
        scanf("%lld %s", &d.my_data1, d.my_data2);
        Ins(d);
    }
    while (m-- && j >= 1)
    {
        printf("%s\n", S[1].my_data2);
        d = S[1];
        d.my_data1 *= 2;
        Extract();
        if (d.my_data1 < p)
        {
            Ins(d);
        }

    }
    return 0;
}







//本题的逻辑结构:优先队列,树、最小堆
//本题的存储结构:线性(顺序)存储

/*
解题思路和算法:1、建立最小堆,按照输入的数据插入最小堆,进行最小堆排序,
                   每次从最小堆中取出最小的数
                2、判断是否存在优先级相同但ascll码相对较小的数据,
                   如果存在则交换两个数据的信息, 并把ascll码相对较大的
                3、数据按原样存入最小堆,对应数据取出后输出字符串,
                   之后将权值加倍并存入最小堆,进行排序,重复执行直到达到规定输出次数

*/

//效率:
//时间复杂度:O(n)
//空间复杂度:S(n) = O(1)


//测试数据:(1)输入:
/*
3 3
1 hello
2 world
10 test
*/

//          (2)输出:
/*
hello
hello
world
*/





12 循环移位(Cycle)

题目描述:

所谓循环移位是指:一个字符串的首字母移到末尾, 其他字符的次序保持不变。比如ABCD经过一次循环移位后变成BCDA。
给定两个字符串,判断它们是不是可以通过若干次循环移位得到彼此。

输入格式:

第一行为一个整数,为判断的次数n;
下面由m行组成,每行包含两个由大写字母'A'~'Z'组成的字符串,中间由空格隔开。

要求:
0 ≤ n ≤ 5000
0 ≤ m ≤ 5000
1 ≤ |S1|, |S2| ≤ 10^5

输出格式:

对于每行输入,输出这两个字符串是否可以通过循环移位得到彼此:YES表示是,NO表示否。

输入样例:

4
AACD CDAA
ABCDEFG EFGABCD
ABCD ACBD
ABCDEFEG ABCDEE

输出样例:

YES
YES
NO
NO

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

参考代码:

#include<bits/stdc++.h>
//载入万能头

using namespace std;

int main()
{
    int n;
    string str1,str2;
    cin>>n;
    for(int i=0;i<n;i++)//输入接下来的n行
    {
        cin>>str1 >> str2;
        int a = str1.length();//字符串1的长度
        int b = str2.length();//字符串2的长度
        bool flag = false;假定是不可以得到的
        if(a == b)//只有长度相同才有机会循环移位得到
        {
            for(int j=0; j<a; j++)//
            {
                //每次按固定长度循环拷贝子字符串后,得到前后拷贝的字符串;
                //如果字符串2的后面部分子字符串和字符串1前面相应长度的子字符串相同
                //而且字符串1的上一步未拷贝的后面部分子字符串和字符串2的上一步前面部分子字符串相同
                //那么字符串1和字符串2是可以通过循环位移得到彼此的
                if(str2.substr(a-j) == str1.substr(0,j) && str1.substr(j) == str2.substr(0,a-j))

                {
                    flag = true;
                    break;
                }
            }
        }
        if(flag)
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
    }

    return 0;
}







//本题的逻辑结构:字符串string
//本题的存储结构:线性(顺序)存储

/*
解题思路和算法:1、先判断两个字符串长度是否相同,若不相同则直接输出NO,否则进行下面的操作
                2、每次按固定长度循环拷贝子字符串后,得到前后拷贝的字符串;
                3、循环移位前,先按照头--->尾、尾-->头分别拷贝两个字符串的字串
                //如果字符串2的后面部分子字符串和字符串1前面相应长度的子字符串相同
                //而且字符串1的上一步未拷贝的后面部分子字符串和字符串2的上一步前面部分子字符串相同
                4、那么字符串1和字符串2是可以通过循环位移得到彼此的

*/

//效率:
//时间复杂度:O(n)
//空间复杂度:S(n) = O(1)


//测试数据:(1)输入:
/*
4
AACD CDAA
ABCDEFG EFGABCD
ABCD ACBD
ABCDEFEG ABCDEE
*/

//          (2)输出:
/*
4
AACD CDAA
YES
ABCDEFG EFGABCD
YES
ABCD ACBD
NO
ABCDEFEG ABCDEE
NO
*/





写在最后

以上代码仅供同学们参考,切勿复制粘贴、1草草了事,东西是给自己学的、对自己负责;时间关系没有给大家做代码注释,希望同学们上课时候认真听讲、做到举一反三。

如有雷同纯属巧合、若有侵权请及时联系删文

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云边牧风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值