蓝桥杯每日一题 (递推、递归、)

3777. 砖块(每日一题)

后面if条件判断很巧妙,只要有一种成立就不管另一种了。

#include<bits/stdc++.h>
using namespace std;
//3777. 砖块
//跟贪心差不多
string s;
int t,n;

void update(char &c)
{
    if(c=='W')c='B';
    else c='W';
}

bool check(char c)
{
    vector<int>a;
    string ss=s;
    for(int i=0;i+1<n;i++)
    {
        if(ss[i]!=c)
        {
            update(ss[i]);
            update(ss[i+1]);
            a.push_back(i);
        }
    }
    if(ss.back()!=c)return false;
    cout<<a.size()<<endl;
    for(int i:a)cout<<i+1<<" ";
    if(a.size()>0)cout<<endl;
    return true;

    }
int main()
{
    cin>>t;
    for(int i=0;i<t;i++)
    {
        cin>>n;
        cin>>s;
        if(!check('B')&&!check('W'))cout<<"-1"<<endl;

    }
}

1208. 翻硬币

和翻砖头一样,(赋值用成了==浪费了10min)

#include<bits/stdc++.h>
using namespace std;
//1208. 翻硬币
string s,t;
void update(int i)
{
    if(s[i]=='*')s[i]='o';
    else s[i]='*';
}
int main()
{
    cin>>s;
    cin>>t;
    int cnt=0;
    for(int i=0;i<s.size();i++)
    {

        
        if(s[i]!=t[i])
        {
            update(i);
            update(i+1);

            cnt++;
        }
    }
    cout<<cnt<<endl;

}

递归 1497 树的遍历

自己的做法如下(是错误的):没做出来:

#include<bits/stdc++.h>
using namespace std;
const int N=30;
//1497. 树的遍历(错误的)
vector<int>a(N);
vector<int>b(N);
vector<int>d(N);
unordered_map<int,int>mp;//某个点是在哪个位置
int n;

void countt(int c,int cnt )
{
    if(cnt>n)return;
    int t=a[c];
    int pos=mp[t];
    d[cnt]=t;
    cnt*=2;
    countt(pos-1,cnt);
    countt(c-1,cnt+1);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    //for(int i=1;i<=n;i++)cout<<a[i]<<endl;
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
        mp[b[i]]=i;
    }
    countt(n,1);
    int num=1,lay=0;
    while(num<=n)
    {
        for(int j=num;j<num+1<<lay;j++)
        {
            cout<<d[j]<<" ";
        }
        lay++;
        num=2<<lay;
    }
}

y做法,递归的参数有四个。每层递归的作用就是找到后序遍历的最后一个结点,也就是根节点。找出其在中序遍历中的位置。相当于进行了一个划分。然后递归求左右子树的根节点,保存。最后返回根节点。递归的条件是:划分之后左子树的中序遍历左端点是否在pos左边,右端点是否在pos右边。

#include<bits/stdc++.h>
using namespace std;
const int N=30;
//1497. 树的遍历
int a[N],b[N];
unordered_map<int,int>l,r,p;//用来保存某个点的左右子树
int n;

//每次返回左子树或者右子树
int build(int ml,int mr,int pl,int pr)
{
   int t=a[pr];//找到根节点的值
   int pos=p[t];//找到根节点的pos

   if(ml<pos)l[t]=build(ml,pos-1,pl,pl+pos-1-ml);
   if(mr>pos)r[t]=build(pos+1,mr, pl+pos-ml,pr-1);
   return t;
}

void bfs(int t)
{
    queue<int>q;
    q.push(t);
    while(!q.empty())
    {
        int u=q.front();
        cout<<u<<" ";
        q.pop();
        if(l.count(u))q.push(l[u]);
        if(r.count(u))q.push(r[u]);

    }


}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)
    {
        cin>>b[i];
        p[b[i]]=i;//保存中序位置
    }
    int t=build(0,n-1,0,n-1);
    bfs(t);


}

//1249. 亲戚

找亲戚问题:我犯的错是:让f【l】=find(r)。我只想到了两个人有亲戚就这样赋值。

其实是错误的,两个人有亲戚是代表着两个大集合之间实现了连通,所以要找到l的祖先赋值为r的祖先,这样就能代表整个集合。否则只有l能和r有亲戚。

#include<bits/stdc++.h>
using namespace std;
const int N=20000;
//1249 亲戚
int f[N];
int findf(int x)
{
    if(f[x]==x)return x;
    else return findf(f[x]);
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=0;i<m;i++)
    {
        int l,r;
        cin>>l>>r;
        f[findf(l)]=findf(r);
    }
    int q;
    cin>>q;
    for(int i=0;i<q;i++)
    {
        int l,r;
        cin>>l>>r;
        if(findf(l)==findf(r))cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }

}

//836合并集合

此题思路没有问题了,但是在输入上有问题。

复习了getcahr();

当我们希望读取一行字符,包括其中的空格的时候一定要加上getchar(),因为前面遗留了一个换行符号。

 cin>>n>>m;
    //getchar();
    //这个时候如果没有getchar,s就会读取换行符号
    getline(cin,s);
    cout<<s;

 getline之间不用加(cin会把换行符留在缓冲区里面,等待被其他变量获取,而getline会读取换行符,并丢弃掉)

cin>>n>>m;
    getchar();
    //这个时候如果没有getchar,s就会读取换行符号
    getline(cin,s);
    getline(cin,s);
    cout<<s;
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//836 合并集合
//(重点是怎么处理字符串)
char s[2];
int n,m;
int f[N];

int getf(int x)
{
    if(f[x]==x)return x;
    else return getf(f[x]);
}


int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i]=i;
    while(m--)
    {
        int l,r;
        scanf("%s%d%d", s, &l, &r);
        if(s[0]=='M')
        {
            f[getf(l)]=getf(r);
        }

        else
        {
            if(getf(l)==getf(r))puts("Yes");
            else puts("No");
        }
    }

}

// 837 连通块中点的数量

做出来了,但是TLE

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//837 连通块中点的数量 TLE
int n,m;
char s[2];
int l,r;
int f[N];

int getf(int x)
{
    if(f[x]==x)return x;
    else return getf(f[x]);
}


int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i]=i;
    while(m--)
    {
      cin>>s;
      if(s[0]=='C')
      {
         cin>>l>>r;
         f[getf(l)]=getf(r);
      }
      else if(s[0]=='Q'&&s[1]=='1')
      {
          cin>>l>>r;
          if(getf(l)==getf(r))
          {
              cout<<"Yes"<<endl;
          }
          else cout<<"No"<<endl;
      }
      else
      {
          int cnt=0;
          cin>>l;
          for(int i=1;i<=n;i++)
          {
              if(getf(i)==getf(l))
              {
                  cnt++;
              }
          }
          cout<<cnt<<endl;
      }
    }
}

y做法:自己的做法的错误:在计算一个连通子块的时候可以用数组存。因为每个子块一定有一个唯一的共同的祖先。用这个点存cnt值。

并查集太博大精深了,以前觉得很好理解,其实里面还有很多细节,在用cnt存的时候,只有两个结点不在同一个集合里面才能,加cnt。否则会重复。

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
//837 连通块中点的数量
int n,m;
char s[2];
int l,r;
int f[N];
int cnt[N];
int getf(int x)
{
    if(f[x]==x)return x;
    else return getf(f[x]);
}


int main()
{
    cin>>n>>m;
 
    for(int i=1;i<=n;i++)f[i]=i,cnt[i]=1;
    while(m--)
    {
      cin>>s;
      if(s[0]=='C')
      {
         cin>>l>>r;
         if(getf(r)!=getf(l))
         {
           f[getf(l)]=getf(r);
           cnt[getf(r)]+=cnt[getf(l)];
         }
         
      }
      else if(s[0]=='Q'&&s[1]=='1')
      {
          cin>>l>>r;
          if(getf(l)==getf(r))
          {
              cout<<"Yes"<<endl;
          }
          else cout<<"No"<<endl;
      }
      else
      {

          cin>>l;
          cout<<cnt[getf(l)]<<endl;
      }
    }



}

240 食物链(带权并查集)

思考时的难点是三个种群的区别。

这道题不是一个种群一个集合了,是一个完整的食物网一个集合。

食物网的每条路一定是按照题目给出的食物三角形走的。所以对于同一个食物网上的两个物种,判断其关系。就要有一个起点,也就是这个食物网的祖先结点。计算到根节点的距离。得到相对距离取模。

对于没有在同一个网上的两个同类物种。一定是对的。(只要我们更新好d)。让x的祖先结点指向y的祖先结点。且更新d为dy-dx。这样就保证捕食关系正确了。

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
//240食物链
int f[N];
int d[N];
int getf(int x)
{
    if(f[x]==x)return x;
    else
    {
        int t=getf(f[x]);
        d[x]+=d[f[x]];//计算到根节点的距离
        //先保存d,再更新f[x]
        f[x]=t;
    }
    return f[x];
}

int main()
{
    int n,m,cnt=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i]=i;
    int res=0;
    while(m--)
    {
        int t,x,y;
        cin>>t>>x>>y;
        if(x>n||y>n)cnt++;
        else
        {
            int px=getf(x);
            int py=getf(y);
            if(t==1)
            {
                if(px==py&&(d[x]-d[y])%3)cnt++;
                else if(px!=py)
                {
                    f[px]=py;
                    d[px]=(d[y]-d[x]);
                }
            }
            else
            {
                //正常情况下应该相差1
               if(px==py&&(d[x]-d[y]-1)%3)cnt++;
               else if(px!=py)
               {
                   f[px]=py;
                   d[px]=d[y]-d[x]+1;
               }
            }
        }
    }
    cout<<cnt;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值