pat链表专题训练+搜索专题

本期题目包括:

1074:https://pintia.cn/problem-sets/994805342720868352/problems/994805394512134144

1052:https://pintia.cn/problem-sets/994805342720868352/problems/994805425780670464

1032:https://pintia.cn/problem-sets/994805342720868352/problems/994805460652113920

1097:https://pintia.cn/problem-sets/994805342720868352/problems/994805375457411072

1103:https://pintia.cn/problem-sets/994805342720868352/problems/994805364711604224

1091:https://pintia.cn/problem-sets/994805342720868352/problems/994805375457411072

 

1074:

题目大意:给定一长度为n的链表,要求每k次反转一次,例如链表为1-2-3-4-5-6,当k=3时,反转结果为3-2-1--6-5-4,当k等于4的时,反转结果为4-3-2-1-5-6;

解题思路:讲链表地址存入顺序表中转置最后输出,这样相比与链表反转算法会少操作狠多

假定长度为10,我们有一个1,2,3,4,5,6,7,8,9,10的链表,且假设k=4,所以说有1-4,5-8被反转,那设他们的下标为i,在满足i+4<=n的条件下是可以被转置的,

例如:i,i+1,i+2,i+3,i+4.......i+k-1;

我们要将反转两数的下标和为2*i+k-1;

两两操作要执行k/2次;

最好转置完后输出即可

参考代码:

 1 #include<bits/stdc++.h>//1074
 2 using namespace std;
 3 #define  IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 4 #define int long long
 5 int n,k,st;
 6 int cnt;
 7 const int N=1e5+10;
 8 struct node
 9 {
10     int key;//键值
11     int ne;//下一个节点的首地址
12 }L[N];
13 vector<int>q;//存放结果
14 signed main()
15 {
16     IOS;
17     cin>>st>>n>>k;
18     for(int i=0;i<n;i++)
19     {
20         int a,b,c;
21         cin>>a>>b>>c;
22         L[a].key=b;
23         //L[c].ne=c;
24         L[a].ne=c;
25     }
26     for(int i=st;~i;i=L[i].ne)//存入地址
27     {
28         q.push_back(i);
29     }
30     for(int i=0;i+k<=q.size();i+=k)//满足每k个转置
31     {
32         //i+0,i+1,i+2.....i+k-1;
33         for(int j=0;j<k/2;j++)//操作k/2轮
34         {
35             int tmp=q[i+j];
36             q[i+j]=q[i+k-j-1];
37             q[i+k-j-1]=tmp;
38         }
39     }
40    for(int i=0;i<q.size();i++)
41     {
42         if(i<q.size()-1)
43         {
44             printf("%05lld %lld %05lld\n",q[i],L[q[i]].key,q[i+1]);
45         }
46         else
47             printf("%05lld %lld -1",q[i],L[q[i]].key);
48     }
49    // for(int i=0;i<q.size();i++)
50         //printf("%05lld ",q[i]);
51     return 0;
52 }

 

1052:

题目大意:给定一个链表要求给其键值排序后输出

题目思路:

题目给出的数据有可能是不存在于链表上的节点即无效节点,我们因此可以设一个flag值表示这个节点是否有效;

首先我们在开始的时候肯定是要把所有的节点初始化为无效,在输入完地址,键值和下一个邻接点的地址之后,我们枚举按照节点连接顺序每个节点地址,并且置该地址的节点为有效值并统计节点有效数目,如果节点的有效数目是0,就输出0和-1就好了

如果不是的话

我们首先要按照题目要求设计排序,这里排序可以这样设计:

我们可以把所有的有效节点放在较为靠前的部分,无效的放在后面,所以说当出现无效节点的时候,可以return a.flag>b.flag,即按照节点的有效性进行排序,如果曼纳祖有效性了,就要按照节点的大小顺序进行排序了,这里可以return a.key<b.key;

然后有多少个有效节点就按照题目要求进行输出即可

 1 #include<bits/stdc++.h>//1052
 2 using namespace std;
 3 #define  IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 4 #define int long long
 5 int cnt;
 6 const int N=1e5+10;
 7 struct node
 8 {
 9     int address;
10     int key;
11     int ne;
12     bool flag;
13 }L[N];
14 bool cmp(node a,node b)
15 {
16     if(!a.flag||!b.flag)//将有效节点放在前面
17     {
18         return a.flag>b.flag;
19     }
20     else
21         return a.key<b.key;//按大小顺序进行排序
22 }
23 signed main()
24 {
25     IOS;
26     for(int i=0;i<N;i++)//init()
27         L[i].flag=false;
28     int n,st;
29     cin>>n>>st;
30     for(int i=0;i<n;i++)
31     {
32         int a,d,e;
33         cin>>a>>d>>e;
34         L[a].address=a;
35         L[a].key=d;
36         L[a].ne=e;
37     }
38     int s=st;//存放首地址
39     while(s!=-1)//没有到末地址
40     {
41         L[s].flag=true;
42         cnt++;//统计有效节点个数
43         s=L[s].ne;
44     }
45     if(!cnt)
46         cout<<0<<" "<<-1<<endl;
47     else
48     {
49         sort(L,L+N,cmp);
50         printf("%lld %05lld\n",cnt,L[0].address);
51         for(int i=0;i<cnt;i++)
52         {
53             if(i<cnt-1)
54                 printf("%05lld %lld %05lld\n",L[i].address,L[i].key,L[i+1].address);
55             else
56                 printf("%05lld %lld -1",L[i].address,L[i].key);
57         }
58     }
59     return 0;
60 }

 

1032:

题目大意:给定两个word,word中每个letter代表一个节点,这个节点有prior,key,next,题目要求找出两个word的相同部分并且输出相同部分第一个letter的site;

解题思路:正常输入后将待检验的word的节点的地址标为已经标记过,在检验数组中如果碰到已经被标为true的标记数组就break,然后输出即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define  IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 4 #define int long long
 5 const int N=1e5+10;
 6 struct node
 7 {
 8     char data;
 9     int ne;
10     bool flag;
11 }le[N];
12 signed main()
13 {
14     IOS;
15     for(int i=0;i<N;i++)
16         le[i].flag=false;
17     int st1,st2,n;
18     cin>>st1>>st2>>n;
19     for(int i=0;i<n;i++)
20     {
21         int a,e;
22         char d;
23         cin>>a>>d>>e;
24         le[a].data=d;
25         le[a].ne=e;
26     }
27     int s;
28     for(s=st1;~s;s=le[s].ne)
29     {
30         le[s].flag=true;
31     }
32     for(s=st2;~s;s=le[s].ne)
33     {
34         if(le[s].flag)
35             break;
36     }
37     if(s!=-1)
38         printf("%05lld",s);
39     else
40         printf("%lld",-1);
41     return 0;
42 }

 

1097:其实这个题以前做过,是天梯赛l2的第一题;

题目大意:其实就是题面意思,给定一链表,要求删除绝对值重复的元素,保留的是开始是没有绝对值与其相等的节点。

题目思路:用两个数组来存储删除和保留的节点,并且设置一个标记数组,如果这个标记数组被标记过了,就是重复的元素将其放入b数组,其余的放入a数组并且标记

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 #define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 5 int st,n;
 6 const int N=1e5+10;
 7 struct node
 8 {
 9     int key;
10     int ne;
11 }L[N];
12 bool vis[N];
13 vector<int>a,b;//a,存储保留的,b存储删除的
14 signed main()
15 {
16     IOS;
17     cin>>st>>n;
18     for(int i=0;i<n;i++)
19     {
20         int add,data,next;
21         cin>>add>>data>>next;
22         L[add].key=data;
23         L[add].ne=next;
24     }
25     for(int i=st;~i;i=L[i].ne)
26     {
27 
28         if(!vis[abs(L[i].key)])//下标是绝对值
29         {
30             vis[abs(L[i].key)]=true;//标记
31             a.push_back(i);
32         }
33         else{
34             b.push_back(i);
35         }
36     }
37     for(int i=0;i<a.size();i++)
38     {
39         if(i<a.size()-1)
40         {
41             printf("%05lld %lld %05lld\n",a[i],L[a[i]].key,a[i+1]);
42         }
43         else
44             printf("%05lld %lld -1\n",a[i],L[a[i]].key);
45     }
46      for(int i=0;i<b.size();i++)
47     {
48         if(i<b.size()-1)
49         {
50             printf("%05lld %lld %05lld\n",b[i],L[b[i]].key,b[i+1]);
51         }
52         else
53             printf("%05lld %lld -1",b[i],L[b[i]].key);
54     }
55     return 0;
56 }

 

 

总结一下关于链表操作的做法:

首先肯定是要写链表的结构,包括他的地址,键值和下一个邻接点的地址,并且根据题目,我们可以设一个链表节点的性质,比如这个链表是不是有效的,是不拴被访问过的等等;

并且我们在输入的时候一般会采用hash输入,即是用节点的前驱地址来表示这个节点的下标,并且,这个做可能会导致链表不连续,这时候根据题目要求就可以进行对有效性的排序,将有效的节点放在前面将无效的节点放在后面,例如上面的1052

1 struct node
2 {
3     int address;
4     int key;
5     int ne;
6     bool flag;
7 }L[N];

 

然后是要对链表初始化,将他的性质全部置为true或者false;

  1 for(int i=0;i<N;i++)

 2 L[i].flag=false; 

第三点是对链表进行地址顺序的遍历,这时候我们就可以对他的性质进行赋值,并且如果有题目要求按照第几个节点输出或者是有统计多少个连续节点的要求就可以统计有效节点的顺序

 1 while(s!=-1)
 2     {
 3         L[s].flag=true;
 4         cnt++;
 5         s=L[s].ne;
 6     }
 7 
 8 
 9 
10 或者是
11 
12 
13    for(s=st1;~s;s=le[s].ne)
14     {
15         le[s].flag=true;
16         cnt++;
17     }

第四点就是题目的具体要求了,按照题目要求在进行操作就可以了

 

 

1103:

题目大意:给定三个数n,k,p;表示用k个数的p次方之和为n,并且,要求输出这k个数的字典序是最大的,例如

当n=169时,有12^2+4^2+2^2+2^2+1^2,11^2+6^2+2^2+2^2+2^2, 6^2 + 6^2 + 6^2 + 6^2 + 5^2;

其中第三个序列的所有底数之和最大,也就是字典序最大,题目要求输出这个最大的序列

题目思路:

dfs

可以先打表,按照下标表示第i个数的k次方,当然是满足这第i个数的k次方要小于等于n;

然后要设置两个数组来表示最优结果序列和当前选择序列;

如果满足条件的情况下并且他的当前序列和是最大的,我们更新最大值并且更新最优序列

另外在设计分支的时候,为保证字典序最大我们可以从打完的表的最大位开始从后往前

选择的话存在选多次,所以如果选择的话下标可以不变,

如果不选的话下标就要减1了因为是从后往前遍历的

所以dfs的主体框架可以这么写:

void dfs(int index,int sum,int cnt,int tsum)
{

    if(sum>n||cnt>k)
    return ;
    if(sum==n&&cnt==k)
    {

    }
    if(index>0)
    {
        
    }
}

其中tsum表示的是当前底数序列的和,sum表示的是前a[i]^k之和

参考代码:

 1 #include<bits/stdc++.h>//1103
 2 using namespace std;
 3 #define int long long
 4 #define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
 5 vector<int>ans,tmp,num;//最优序列,当前序列,表
 6 int n,k,p;
 7 int maxnsum=-1;
 8 int caluate(int u)//计算k次方
 9 {
10     int a=1;
11     for(int i=1;i<=p;i++)
12     {
13         a=a*u;
14     }
15     return a;
16 }
17 void init()//打表
18 {
19     int i=0;
20     int digit=0;
21     while(digit<=n)
22     {
23         num.push_back(digit);
24         i++;
25         digit=caluate(i);
26     }
27 
28 }
29 void dfs(int index,int sum,int cnt,int tsum)
30 {
31 
32     if(sum>n||cnt>k)//大于了就不满足了
33     return ;
34     if(sum==n&&cnt==k)
35     {
36         if(tsum>maxnsum)//更新最大值
37         {
38             maxnsum=tsum;
39             ans=tmp;//更新最优序列
40         }
41     }
42     if(index>0)
43     {
44         //
45         tmp.push_back(index);//将当前底数存入
46         dfs(index,sum+num[index],cnt+1,tsum+index);
47         //不选
48         tmp.pop_back();
49         dfs(index-1,sum,cnt,tsum);
50     }
51 }
52 signed main()
53 {
54     IOS;
55     //init();
56     cin>>n>>k>>p;
57     init();
58     dfs(num.size()-1,0,0,0);//倒着来选出字典序最大的序列
59     if(maxnsum<0)
60     {
61         printf("Impossible");
62     }
63     else
64     {
65         printf("%d = ",n);
66         for(int i=0;i<ans.size();i++)
67         {
68             if(i<ans.size()-1)
69             {
70                 printf("%d^%d + ",ans[i],p);
71             }
72             else
73             printf("%d^%d",ans[i],p);
74         }
75     }
76     return 0;
77 }

 

 

1091:

题目大意:

题目背景是基于脑中风,要求卒中区域的最大体积

题目给定了n张脑中风切片,并且每张脑中风切片是一个m*n的矩阵,脑卒区域是1,正常取余是2

并且卒中区域是一个多个相邻脑卒区域构成的,这里的相邻就是前后上下左右相邻,只要相邻的多余t个,就构成卒中区域,否则就不算

题目要求的是卒中的最大面积

题目思路:

其实已经很明朗了,经典三维bfs了,和杭电那道题已经算是高度相似了:https://www.cnblogs.com/LQS-blog/p/16265466.html

需要注意的依旧是那几点:

注意标记,注意统计,注意判断函数怎么写

参考代码:

 1 #include<bits/stdc++.h>//1091
 2 using namespace std;
 3 #define int long long
 4 #define IOS ios_base::sync_with_stdio(0);;cin.tie(0);cout.tie(0);
 5 int m,n,l,t;
 6 int mapp[1300][140][100];
 7 struct node
 8 {
 9     int x,y,z;
10 };
11 queue<node>q;
12 bool vis[1300][140][100];
13 int X[6] = {1, 0, 0, -1, 0, 0};
14 int Y[6] = {0, 1, 0, 0, -1, 0};
15 int Z[6] = {0, 0, 1, 0, 0, -1};
16 bool check(int nx, int ny, int nz) {
17      if (nx >= m || nx < 0|| ny < 0|| ny >=n||nz<0 || nz >=l ) 
18      return false;
19      if (mapp[nx][ny][nz] == 0 || vis[nx][ny][nz] == true)
20      return false;
21      return true;
22  }
23 int bfs(int dx,int dy,int dz)
24 {
25     int cnt=1;//起点也包括在内
26     vis[dx][dy][dz]=true;//标记被访问过
27     queue<node>q;
28     node start,next;
29     start.x=dx;
30     start.y=dy;
31     start.z=dz;
32     q.push(start);
33     while(!q.empty())
34     {
35         start=q.front();
36         q.pop();
37         for(int i=0;i<6;i++)
38         {
39             next.x=start.x+X[i];
40             next.y=start.y+Y[i];
41             next.z=start.z+Z[i];
42             if(check(next.x,next.y,next.z))
43             {
44                 vis[next.x][next.y][next.z]=true;
45                 cnt++;
46                 q.push(next);
47             }
48         }
49     } 
50     if(cnt>=t)//构成卒中区域
51     return cnt;
52     else
53     return 0;
54 }
55 signed main()
56 {
57     IOS;
58     cin>>m>>n>>l>>t;
59     for(int k=0;k<l;k++)
60     {
61         for(int i=0;i<m;i++)
62         {
63             for(int j=0;j<n;j++)
64             {
65                 cin>>mapp[i][j][k];
66             }
67         }
68     }
69     int ans=0;
70     for(int k=0;k<l;k++)
71     {
72         for(int i=0;i<m;i++)
73         {
74             for(int j=0;j<n;j++)
75             {
76                 if(mapp[i][j][k]==1&&!vis[i][j][k])//没有被访问过并且是脑卒区域
77                 {
78                     ans+=bfs(i,j,k);
79                 }
80             }
81         }
82     }
83         cout<<ans;
84     return 0;
85 }

 

 

 

 

posted @ 2022-08-09 17:15  江上舟摇  阅读(39)  评论(0编辑  收藏  举报