上午
写二叉树题目,“P1827 [USACO3.4]美国血统 American Heritage”,题目描述为:
通过的代码为:
#include <iostream>
#include <cstring>
using namespace std;
void backi(string m,string f)
{
if(f.empty)//当前此为前序遍历序列分割出的左子树序遍历序列为空的时候,代表此子树已经分割完
return;
char s=f[0];//前序遍历序列的首位字符,即为此树枝的根节点
int i=m.find(s);//在中序遍历中找到此根节点的位置,已知根节点的两边分别为它的左右子树,以此分割区域
f.erase(f.begin());//抹去根节点,使得更好的分割
string lf=f.substr(0,i);//此为前序遍历序列分割出的左子树
string rf=f.substr(i);//此为前序遍历序列分割出的右子树
string lm=m.substr(0,i);//此为中序遍历序列分割出的左子树
string rm=m.substr(i+1);//此为中序遍历序列分割出的右子树
backi(lm,lf);backi(rm,rf);//在递归中将子树中的节点一步步分割,直到前序遍历序列为空,
//这代表以“前序遍历”得到的“根节点”已经在递
//归中得到,且可以按照后序遍历方式输出了。
cout <<s;
}
int main()
{
string mid,fro;
cin>>mid>>fro;//得到中序遍历和前序遍历序列
backi(mid,fro);//进入函数
return 0;
}
//这里解释一下为什么是从前序遍历中得出后序遍历的输出。
//可以知道,前序遍历是先输出节点再遍历左子树和右子树,即根左右,序列中
//以左优先,而后序遍历输出也是以左节点优先。前序遍历序列靠后的部分要么是前一个节点的子节点,
//要么就是一个节点同父节点的右儿子。
//以此为基本,则在中序遍历条件下判断左右子树,于是可以得到后序遍历序列
核心思想是,从中序遍历序列中可得到根节点的左右子树,而已知前序序列的第一个节点为根节点。于是我们根据从中序遍历序列中得到的左右子树把前序遍历序列中的“左右子树”——就是包含着左右子树的串——截出来,使用递归,借助前序遍历序列的首字母总是根节点的特性一步步求出每一个截出的部分的“根节点”,最后使用后序遍历输出的方法,将得出的“根节点”输出即可。
下午
听课,听学长讲解二叉树和并查集的知识点,有收获。
写出一个题。
通过的代码是:
#include <iostream>
#include <cstring>
using namespace std;
int pre[10000];
int findi(int x)//这个函数的作用是找出x的根节点
{
if(pre[x]==x)//如果某个书数的父节点是它本身,按照我主函数里原本设置好的初值,那么这就是根节点了
return x;
return pre[x]=findi(pre[x]);//如果还不是根节点,那就递归找出根节点,即某数父节点的父节点的父节点这样一步步递归下去
//将得到的根节点设置为x的父节点,这是在减少时间复杂度
}
int main()
{
int n,m,p,m1,m2;
cin>>n>>m>>p;
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=0;i<m;i++)
{
cin>>m1>>m2;
pre[findi(m2)]=findi(m1);//将m2的根节点的父节点设置为m1的根节点,也就是连接两个子集
}
for(int i=0;i<p;i++)
{
cin>>m1>>m2;
if(findi(m1)==findi(m2))//如果两个数的根节点是一样的,就代表是亲戚关系了
printf("Yes\n");
else printf("No\n");
}
}
理解了之后就挺容易的,对于目前我写了的并查集来说……
核心思想是关于根节点的设立。将这题抽象成“树”的生成,如果两个人是亲戚的话,那么就设定他们存在于同一个根节点之下。自此就可以解出此题。实现的重点在代码的注释里。
晚上
写了个并查集的模板题:
得出的代码为:
#include <iostream>
#include <cstring>
using namespace std;
int pre[10000];
int findi(int x)
{
if(pre[x]==x)
return x;
return pre[x]=findi(pre[x]);
}
int main()
{
int n,m,z,x,y;
cin >>n>>m;
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=0;i<m;i++)
{
cin >>z>>x>>y;
if(z==1)
pre[findi(y)]=findi(x);
else {
if(findi(x)==findi(y))
cout <<"Y\n";
else cout <<"N\n";
}
}
}
这个题,看下午的那个题的解释就好了,那个明白了,这个就显得简单了。
写了一个上次周测的题目。
通过的代码是:
#include <stdio.h>
#include <string.h>
int main()
{
char a[20],b[20];
gets(a);gets(b);
if(strlen(a)!=strlen(b))
{printf("WRONG_ANSWER");return 0;}//如果连长度都不相同那肯定是不一样的了
for(int i=0;i<strlen(a);i++)//从小到大排序
for(int j=i+1;j<strlen(a);j++)
if(a[i]>a[j])
{
char t=a[i];
a[i]=a[j];
a[j]=t;
}
if(a[0]=='0')//如果原数中有‘0’,那么就找到第一个不是‘0’的位置,然后交换
{
int i=0;
while(a[i]=='0'&&i<strlen(a))
{
i++;
}
if(i<strlen(a))
a[0]=a[i],a[i]='0';
}
if(strcmp(a,b)==0)//最后比较一下是否是相同的数目
printf("OK");
else printf("WRONG_ANSWER");
}
讲真,题目翻译的不是很清晰,给我整麻了……真不是什么难题,就是不清楚题目细节要求,就是那个“前导零”。
明日计划
争取3道+的二叉树、并查集题目,在写一道+的周测题。