大二博客总结(第三周)
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大二博客总结(第三周)相关的知识,希望对你有一定的参考价值。
本周的学习内容:
1.学习了并查集,二叉树,做了配套的题目巩固
2.剩余时间在刷洛古的题目。每类题单都有刷几道(还没根据模块具体刷)
3.利用题目复习线性DP,贪心
并查集
模板题:n个人,m组朋友关系,能形成几个团体。
并查集的操作可分为:
1.初始化(也就是把每个点设置为独立的集,每个对象都是不想交的集合)
void init(int n) //初始化
{
for(int i=1;i<=n;i++)
a[i]=i;
}
2.合并(由于某种属性连接在一起,进行集的合并,然后用某个元素来代表这个集合,称为代表元)
int un(int x,int y)
{
x=find1(x);
y=find1(y);
if(x!=y)
a[x]=a[y];
}
3.查找(通过节点一层层向上找,直到元素的值和它的集相等,称为根节点)
int find1(int x)
{
if(x!=a[x])
x=find1(a[x]); //查找根节点
return x;
}
一般合并的时候需要查找,find_()函数写在union_()函数前面。而最后根节点的数量就是集的数量。
查找、合并----路径压缩:
查找:在递归返回时将所有的中间节点都指向根节点,这样能省去多次的向上查询。
int find1(int x)
{
if(x!=a[x])
a[x]=find1(a[x])
return a[x];
}
合并:对每一个根节点设置一个权值来标记树的高度,为了使得合并后的树不产生退化,就要使得权值小的树称为权值大的树的分支。
int un(int x,int y)
{
x=find1(x);
y=find1(y);
if(x==y) return 0;//根节点相同,不需要合并
if(h[x]>h[y])
a[y]=a[x];
else
{
if(h[x]==h[y])
h[y]+=1; //要和h[x]小于h[y]的情况一起考虑
a[x]=a[y];
}
}
二叉树
深度优先遍历:(运用深搜)
先序遍历(父节点、左儿子、右儿子)
void preorder(node *root) //求先序排列
{
if(root!=NULL)
{
post[k++]=root->value;
preorder(root->l);
preorder(root->r);
}
}
中序遍历(左儿子、父节点、右儿子)
void inorder(node *root)
{
if(root!=NULL)
{
inorder(root->l);
post[k++]=root->value;
inorder(root->r);
}
}
后续遍历(左儿子、右儿子、父节点)
void postorder(node *root)
{
if(root!=NULL)
{
postorder(root->l);
postorder(root->r);
post[++k]=root->value;
}
}
查找后序的建树方式:
由于先序的第一个值为根节点,利用中序便可划分为左孩子、后孩子。不断查找相等的值,再根据递归进行分类,便将树建成。而后序函数只是起到打印输出的作用。关键点:还是找到规律,然后建树
void buildtree(int l,int r,int &t,node* &root)
{
int f=-1;
for(int i=l;i<=r;i++)
{
if(pre[t]==in[i])
{
f=i;break;
}
}
if(f==-1) return;
root=new node(in[f]);
t++;
if(f>l) buildtree(l,f-1,t,root->l);
if(f<r) buildtree(f+1,r,t,root->r);
}
洛古1090
合并果子,第一个想法是DP,与合并石子相似。由于果子合并会生成一个新堆,需要不断排序,但这样会超时。因此采用优先队列,每次取得两个值,然后放进去一个,不断累加体力值即可。
优先队列:(从小到大取出)
priority_queue<int,vector<int>,greater<int> >q; // 从小到大
priority_queue<int,vector<int>,less<int> >q; //从大到小
洛古 P3817
把题目想复杂了。正解:先将每碓果子多余x的部分先拿走,然后从第二堆开始,加上前一堆的糖果,再拿走多余x的部分。我刚开始做法只关注偶数堆的糖果,保证这堆各自加上周围堆糖果数,不超过x;然后将堆数是偶数的情况中,保证最后一堆也满足条件。
P1106 删数问题
一开始就掉坑里了,以为删除字符串中最大的几个数,剩下的数组合自然最小。
eg: 1529 若是这个思路删除的是‘9’,变成152;但129才是最小的,应该删除5.
所以共要删除k的数,应该从字符串前面开始删除,如果前面的数字大于后面的,则删除他,后面数字向前移动一位。
此外还要考虑到前导0的情况,存在的一个小坑是:
值若是10000,则删除1,剩下的是个0,字符串长度也为4,所以删除的时候要加上g<len
while(a[g]=='0'&&g<len) g++;
最大的坑是必须是字符串形式,不能转化为整数形式,实数是有范围的。不然最后一组样例超时。。。。
P1435 [IOI2000]回文字串 / [蓝桥杯2016省]密码脱落
给出一个字符串,要求添加最少的字母,使它变为回文字符串
思路:首先明确,在前面添加的字母肯定是末尾出现过但前面没有的字母;在后面添加的肯定是前面出现过但后面没有的字母。因此想到把字符串翻转对比,两个都出现过的字母不用管,添加的字母肯定是是不同的,找出规律:字母长度-相同字母的个数
转化为求最长公共子序列问题:
for(int i=1;i<=len;i++)
{
for(int j=1;j<=len;j++)
{
if(s[i]==s1[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
以上是关于大二博客总结(第三周)的主要内容,如果未能解决你的问题,请参考以下文章