目录
题目A 手撕STL sort (40 分)
函数接口定义:
void sort(int *R, int n);
void sort(int *R,int n)
{
R[n+1]=100000000;//将n+1赋值成无穷大
value = 2*log(n)/log(2);//计算递归层数上限
printf("depth_limit:%d\n",value);
QSort(R,1,n,0);//快速排序
printf("Intermediate:");//无论是否插入排序,都要输出
for(int i = 1;i<=n;i++)
{
printf("%d ", R[i]);
if(i==n)printf("\n");
}
if(flag==1)
{
insertSort(R,n);
}
return;
}
解题思路:
本题的目的是实现C++STL库中的sort()排序,它只是简单的排列整数,但是也让我们感受了一波sort的内部实现过程:最开始先快排,在递归层数达到限制2*logn/log2时,在本层对子数组进行堆排序,(防止快排退化)若分划子数组长度可以减小达到threshold值,(插入排序更加合适)做标记,记得最后进行插入排序。值得注意的是:无论是否进行插入排序,"Intermediate:"必然要输出。解题思路题目中也已经给出,我们直接看代码吧。
#include<iostream>
#include<stdlib.h>
#include<math.h>
using namespace std;
int threshold;
int b[50010];
int value;
int flag = 0;
void restore(int f,int e){ //大根堆
int j=f;//指向树根
int m;
while(j<=e/2)
{
if(2*j<e&&b[2*j]<b[2*j+1]) m=2*j+1;
else m=2*j;
if(b[m]>b[j])
{
swap(b[m],b[j]);
j=m;
}
else j=e;
}
}
void heapSort(int *R,int m,int n)//堆排序
{
//初始建堆
int len= n- m +1;
for(int i=len/2;i>=1;i--){
restore(i,len);
}
printf("Heap:");//输出初始建堆的子数组
for(int i = 1;i <= len;i++)
{
printf("%d ",b[i]);
if(i==len)printf("\n");
}
// 堆排序
for(int i=len;i>=2;i--)
{
swap(b[1],b[i]);
restore(1,i-1);
}
return;
}
int Partition(int *R,int m,int n)
{//分划函数,按照要求,以第一个元素为基准元素
int i = m, j = n+1, k = R[m];
while(i<j)
{
i++;
while(R[i]<=k){i++;}
j--;
while(R[j]>k){j--;}
if(i<j){swap(R[i],R[j]);}
}
swap(R[m],R[j]);
return j;
}
void QSort(int *R,int m,int n,int depth)
{
if(m<n)
{
if(n-m+1<=threshold)
{
flag = 1;//若分划可以达到threshold值,记得最后插入排序
return;
}
else if(depth==value)
{
int Length = n-m+1;
int p =m;
for(int i =1;i<=Length;i++)
{
b[i] = R[p++];
}
heapSort(b,1,Length);
p= m;
for(int i =1;i<=Length;i++)
{
R[p++] = b[i];
}
return;
}
else
{
int j = Partition(R,m,n);
QSort(R,m,j-1,depth+1);
QSort(R,j+1,n,depth+1);
return;
}
}
}
void insertSort(int *R,int n)
{
int j;
int value0;
for(int i = 2;i<=n;i++)
{
for (j = i - 1; j >= 0; j--)
if (R[i] > R[j])break;
if (j != i - 1)
{
value0 = R[i];
for (int k = i - 1; k > j; k--)
R[k + 1] = R[k];
R[j + 1] = value0;
}
}
}
void sort(int *R,int n)
{
R[n+1]=100000000;//将n+1赋值成无穷大
value = 2*log(n)/log(2);//计算递归层数上限
printf("depth_limit:%d\n",value);
QSort(R,1,n,0);
printf("Intermediate:");
for(int i = 1;i<=n;i++)
{
printf("%d ", R[i]);
if(i==n)printf("\n");
}
if(flag==1)
{
insertSort(R,n);
}
return;
}
int main()
{
int n,i;
int a[50010];
scanf("%d %d", &n, &threshold);
for (i = 1; i <= n; i++)
scanf("%d", &a[i]);
sort(a,n);
printf("Final:");
for (i = 1; i <= n; i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
题目B 冬奥会接驳车 (40 分)
解题思路+注意:
这道题就是典型的迷宫问题 ,站在某一块空地上,只能朝前后左右四个方向走去,第一个思路应该是回溯法+深度优先搜索,但是深度优先搜索对于找最短路径迷宫问题,深搜就有些效率不高,因为找到一条通路我们也没法保证他就是最短的路径。如果用广度优先遍历的话,就可以一层一层找,将队列头部节点所有符合情况的儿子节点放入队列,如果出现到达目的地的情况,显然就是最短情况。
题目中用到的数据结构是队列,由于不允许使用STL,所以我简单模拟了Queue,first是头指针,rear是尾指针。
需要注意到的是,要防止数组访问越界,因为不是对任意一块空地,都可以朝四个方向走,并且为了优化,我做了一个标记,记录到达此块空地的方式,例如:若到某块空地是由上一步向下移动得来的,那么从这块空地出发时,就不再向上了;若到某块空地是由上一步向左移动得来的,那么从这块空地出发时,就不再向右了。虽然进入过队列的节点之后也一定不会再进入队列,因为box[i][j]值被赋值-1,变成了不可走的格子,但是通过做标记跳过一种情况可以相对减少一些赋值和比较操作,更节省时间。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int box[102][102];
struct site
{
int i;
int j;
int ceng ;//层数
int num0;//标记自己是怎么得来的。
};
typedef struct
{
site data[10005];
int first ;
int rear;
}Queue;
Queue store;
int path(int n, int m,int x1,int y1,int x2,int y2)
{
int i,j;
int Case;
store.first = 0;
store.rear = 0;
store.data[store.rear].i = x1;
store.data[store.rear].j = y1;
store.data[store.rear].ceng = 0;
store.data[store.rear].num0 = -1;
store.rear++;
box[x1][y1] = -1 ;//入队列标记负值
while(store.first != store.rear)
{
Case = -1;
while(Case<4)
{
Case++;
if(Case==(store.data[store.first].num0+2)%4)continue;//若它由向下移得来,则不考虑向下移动
switch(Case)
{
case 0: {i = store.data[store.first].i;j = store.data[store.first].j-1;break;}//向上
case 1: {i = store.data[store.first].i+1;j = store.data[store.first].j;break;}//向右
case 2: {i = store.data[store.first].i;j = store.data[store.first].j+1;break;}//向下
case 3: {i = store.data[store.first].i-1;j = store.data[store.first].j;break;}//向左
}
if((box[i][j]==0)&&i>=0&&j>=0)
{
store.data[store.rear].i = i;
store.data[store.rear].j = j;
store.data[store.rear].num0 = Case;//记住它是怎么得来的
store.data[store.rear].ceng = store.data[store.first].ceng+1;
box[i][j] = -1 ;//入队列标记负值
store.rear = (store.rear+1)%10000;
}//找到可以走的格子
else if(i == x2 && j == y2)
{
return store.data[store.first].ceng + 1;
}
}
//box[store.data[store.first].i][store.data[store.first].j] = 0;
store.first = (store.first+1)%10000;
}
return 0;
}
int main()
{
int n,m;
int x1,y1,x2,y2;
while((scanf("%d %d",&n,&m))!=EOF)
{
memset(box,1,sizeof(box));
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
scanf("%d",&box[i][j]);
if(box[i][j]==3){x1=i,y1=j;}
if(box[i][j]==4){x2=i;y2=j;}
}
}
int sum = path(n,m,x1,y1,x2,y2);
if(sum==0)
{
printf("unreachable\n");
}
else
{
printf("%d\n",sum);
}
}
return 0;
}
题目C 自动纠错 (20 分)
解题思路:
这道题的整体思路已经给出,
建字典树(buildTree(int n)):
每存一个单词,都从树的节点开始遍历,然后计算经过节点与所存单词的编辑距离冷,接着访问第len个儿子,直到遍历到空指针。把此单词存入。
检索(queryWord(char str[],node *root,int d))
递归检测,当len==0,证明找到单词,然后做标记结束递归。当len<=d,符合条件,然后选取最大频率的。
求解编辑距离(stringMatch_MaxSize(char *str1,char *str2))
关键的算法:计算两个字符串的距离(即两个字符串的编辑距离)。
详细见题目中的详细思路,具体实现看代码。
代码如下:
#include<bits/stdc++.h>
using namespace std;
struct node
{
char str[20];
int num;
node *next[20]={NULL};
};
node * treeRoot = new node; //字典树的根节点
int maxNum = 0;//最大频率
char similarWord[20];//保留需要输出的单词
int flag = 0;//标记是否距离有<=d的单词或者单词本身
struct dot
{
char str[20];
int num;
};
dot word[10010];
int stringMatch_MaxSize(char *str1,char *str2)
{
//计算编辑距离
int max_len[20][20];
memset(max_len,0,sizeof(max_len));
int len1 = strlen(str1);
int len2 = strlen(str2);
//初始化
for(int i = 1; i<=len1; i++)
{
max_len[i][0]= i;
}
for(int i = 1; i<=len2; i++)
{
max_len[0][i] = i;
}
for(int i = 1;i<=len1;i++)
{
for(int j = 1; j<=len2; j++)
{
if(str1[i-1]==str2[j-1])
{
max_len[i][j] = max_len[i-1][j-1];
}
else
{
if(max_len[i][j - 1] < max_len[i-1][j])max_len[i][j] = max_len[i][j- 1];
else max_len[i][j] = max_len[i-1][j];
if (max_len[i][j] > max_len[i - 1][j - 1]) max_len[i][j] = max_len[i - 1][j - 1];
max_len[i][j] += 1;
}
}
}
return max_len[len1][len2];
}
void buildTree(int n)
{
node * p;
for(int i=1;i<n;i++)
{
//printf("treeRoot:%s\n",treeRoot->str);
//printf("word[%d]:%s\n",i,word[i].str);
p = treeRoot;
int len = stringMatch_MaxSize(p->str,word[i].str);
while(p->next[len]!=NULL&&len>0)
{
//printf("len:%d\n",len);
p = p->next[len];
len = stringMatch_MaxSize(p->str,word[i].str);
}
if(len == 0)continue;
if(p->next[len]==NULL)
{
p->next[len] = new node;
strcpy(p->next[len]->str,word[i].str);
p->next[len]->num = word[i].num;
}
}
}
void queryWord(char str[],node *root,int d)
{
if(root==NULL)return;
int len = stringMatch_MaxSize(root->str,str);
if(len == 0)
{
//printf("找到了!!!!\n");
flag=1;
memset(similarWord,'\0',sizeof(similarWord));
strcpy(similarWord,root->str);
return;
}
else if(len<=d)
{
//printf("长度小,欸嘿!!!:%d\n",len);
flag=2;
if(maxNum<root->num)
{
maxNum=root->num;
memset(similarWord,'\0',sizeof(similarWord));
strcpy(similarWord,root->str);
}
else if(maxNum==root->num)
{
if(strcmp(similarWord,root->str)>0)
{
memset(similarWord,'\0',sizeof(similarWord));
strcpy(similarWord,root->str);
}
}
}
int m1;
if(len-d<=0){m1=1;}
else {m1 = len-d;}
int m2;
if(len +d >15){ m2 = 15;}
else{m2 = len +d;}
for(int i = m1; i<= m2 ; i++)
{
if(flag==1)break;
//printf("开始搜索字子树!!!\n");
queryWord(str,root->next[i],d);
}
//printf("递归呀!!\n");
return;
}
int main()
{
int n,m,d;
scanf("%d%d%d",&n,&m,&d);
for(int i = 0;i < n; i++)
{
scanf("%d%s",&word[i].num,word[i].str);
}
strcpy(treeRoot->str,word[0].str);//初始化根节点
treeRoot->num = word[0].num;
buildTree(n);
for(int i = 0; i < m; i++)
{
char findWord[20];
memset(findWord,'\0',sizeof(findWord));
memset(similarWord,'\0',sizeof(similarWord));
flag = 0;
maxNum = 0;
scanf("%s",findWord);
queryWord(findWord,treeRoot,d);
if(flag>=1)printf("%s\n",similarWord);
else{printf("No similar word in dictionary\n");}
}
return 0;
}
请勿抄袭哈!