学习四天,也算是把这周要学的算法学完了,这次就总结一下这几天刷的题吧
本次总结所写题目题解:
1.P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
2.P2078 朋友 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
3.P1827 [USACO3.4] 美国血统 American Heritage - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
4.P1229 遍历问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目链接:
P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P1551 亲戚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这两个题可以说基本一样,我就只列出一个题的代码了
题目简述:
解题思路:因为刚学完并查集,而且这也是并查集对应的题,所以马上就能想到用并查集,一个并查集的模板题,直接将模板写上来
代码实现:
#include<stdio.h>
#define N 10001
int f[N];
int fun(int x)//找某个数的祖宗
{
if(f[x]==x)
return x;
else
{
f[x]=fun(f[x]);//压缩路径,省去重复的找祖宗过程
return f[x];
}
}
int main()
{
int n,t;
scanf("%d %d",&n,&t);
//先将每个数的祖宗变成自己
for(int i=1;i<=n;i++)
f[i]=i;
while(t--)
{
int z,x,y;
scanf("%d %d %d",&z,&x,&y);
//将两个数合并到一个集合
if(z==1)
f[fun(y)]=f[fun(x)];//将x的祖宗赋值给y,说明这两个数合并成一个集合
//判断两个数是否再一个集合
else
{
if(fun(x)==fun(y))//判断它们的祖宗是否相同,相同就是一个集合
printf("Y\n");
else
printf("N\n");
}
}
return 0;
}
该题总结:对于这个模板题,直接套模板就可以了,但是我不知道为什么我第一次写出来的代码能过这个题,明明超时了,但是洛谷没判错,后面我看了题目要求,时间限制是一秒,我去看了啊哈算法,里面有个“路径压缩”,这是一个对于时间的一个巨大优化没有“路径压缩”的代码按道理是过不了的,要进行“路径压缩”才能把时间控制在题目要求范围内
题目链接:P2078 朋友 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目简述:
解题思路:同样与上面的题是用并查集,只是这里不同的是要写出两个找“查”的函数,因为小红和小明在不同的公司,要用不同的数组来表示他/她们的朋友,写出两个“查”函数后,将输入的朋友关系进行合并,最后算出小明的朋友的数和小红的朋友的数,判断哪个朋友数少就输出哪个(因为题目要求求出最多情侣数)
代码实现:
#include<stdio.h>
#define N 10001
int a[N],b[N];
int fun1(int x)//用于“A"公司
{
if(a[x]==x)
return x;
else
{
a[x]=fun1(a[x]);//压缩路径,找过以后不用再找,减少了递归次数,节约时间
return a[x];
}
}
int fun2(int x)//用于“B”公司
{
if(b[x]==x)
return x;
else
{
b[x]=fun2(b[x]);//同上
return b[x];
}
}
int main()
{
int n,m,p,q;
scanf("%d %d %d %d",&n,&m,&p,&q);
//初始化,很重要!!!
for(int i=1;i<=n;i++)
a[i]=i;
for(int j=1;j<=m;j++)
b[j]=j;
while(p--)
{
int xi,yi;
scanf("%d %d",&xi,&yi);
a[fun1(yi)]=a[fun1(xi)];//将输入的朋友进行合并
}
while(q--)
{
int xi,yi;
scanf("%d %d",&xi,&yi);
b[fun2(-yi)]=b[fun2(-xi)];//同上
}
int sum1=0,sum2=0;//sum1用来记录“A”公司小明的朋友,sum2用来记录“B”公司小红的朋友
for(int i=1;i<=n;i++)
if(fun1(i)==fun1(1))//判断是否为小明的朋友或者是小明朋友的朋友。。。。。。
sum1++;
for(int j=1;j<=m;j++)
if(fun2(j)==fun2(1))//判断是否为小红的朋友或者是小明朋友的朋友。。。。。。
sum2++;
//判断能组成的最大情侣对数
if(sum1>sum2)
printf("%d",sum2);
else
printf("%d",sum1);
return 0;
}
该题总结:我写这个题的时候在最后判断小明的朋友的时候写出的语句是下面这个,跑起来是错的,但是我就是默认了小明就是“祖宗”没有考虑到其他的情况,实际上在合并的过程中,小明不一定已知都是“祖宗”,也就是不一定a[1]==1,也会有其他情况,所以这里我们要调用函数去找到小明的“祖宗”,不能就写成1!!!
if(fun1(i)==1)
题目链接:P1827 [USACO3.4] 美国血统 American Heritage - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目简述:
解题思路:实话说,这题我写了三天(我一直找不到左子树的右边界和右子树的左边界),直到昨天,偶尔看到一篇博客 ,看到了求解过程。思路其实很简单,就是把先序遍历的根结点带到中序遍历里面将中序遍历分成左右两节颗子树,先序遍历里面的结点带到左右子树,将该结点的左右子树求出来,直到中序遍历被分解完(这是一个递归过程),按后序遍历的遍历方式线先输出先输出左子树,在输出右子树,最后输出结点就可以了。难点就在找左子树的右边界和右子树的左边界
代码实现:
#include<stdio.h>
#include<string.h>
char z[30],q[30];
void f(int ql,int qr,int zl,int zr)
{
if(ql>qr||zl>zr)//剪枝
return ;
for(int i=zl;i<zr;i++)
if(z[i]==q[ql])//找结点
{
f(ql+1,ql-zl+i+1,zl,i);//遍历左子树
f(qr-zr+i+1,qr,i+1,zr);//遍历右子树
printf("%c",z[i]);//打印
}
//通过确定左右子树区间个数来确定左子树的右边界和右子树的左边界
}
int main()
{
scanf("%s",z);
scanf("%s",q);
int len=strlen(z);
f(0,len,0,len);
return 0;
}
该题总结:左子树右边界和右子树左边界求法
//在中序遍历中找点结点后可以确定左右子树在中序遍历的位置
//将中序遍历的右边界减去结点所在的位置得到右子树的区间个数,再用后序遍历的右边界减去区间数即可得到右子树的左边界
//同理,将结点所在位置减去中序遍历的左边界得到左子树的区间个数,再用后序遍历的左边界加上区间数即可得到左子树的右边界
题目链接:P1229 遍历问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述:
解题思路:首先我们要知道中序遍历是怎么给遍历法(左根右),然后可以谁便找一个二叉树来确定一下什么情况下,告诉你二叉树的先序和后序遍历,求中序遍历的种数(肯定是有规律的吧,不然出这题干嘛),经过几十分钟的推导也是找出来了,当一个结点只有一个子树的时候,这个时候中序遍历可以有多钟形式,所以,这道题我们只需要求出有多少个结点只有一个子树,然后将结点数乘2;(因为这个子树只能是左子树或者是右子树两种情况)
代码实现:
#include<stdio.h>
#include<string.h>
int main()
{
char s1[1000],s2[1000];
scanf("%s%s",s1,s2);
int len=strlen(s1),res=1;
for(int i=0;i<len;i++)
for(int j=0;j<len;j++)
if(i+1>=len||j-1<0)
continue;
else if(s1[i]==s2[j]&&s1[i+1]==s2[j-1])//判断结点是否只有一个子树
res*=2;
printf("%d",res);
return 0;
}
该题总结:遇到这种题目不要慌,找出其规律就可以写出来,代码很简单,就是看能不能找出规律
最后总结:这几天大部分时间都在写题,还有几个题就没有写在总结里面了(感觉和其他题目很像)差不多的思路;这周题单看面还有几个有点棘手的题目还没写出来,这几天争取写出来吧