(23-24赛季)稠州常规赛02题解

· · 个人记录

T1 循环节的判定

思路

本题就是水题,模拟一边即可,解法有很多种,自己尝试!

T2 逛画展

思路1

本题类似与数列分组,那么可以考虑二分,因为这种众多解中求解最优解的题目,多数和二分有关,我们二分一下长度,然后写一个check函数对这个长度是否能看完所有画家的画进行验证,时间复杂度为O(NlogN).

思路2

当然本题可以用双指针来做,因为框定所有的画是题目要求的。

在这种情况下我们可以利用L,R前后指针进行区间纳入数值! 时间复杂度是O(N)!

//k记录当前区间中有多少画家的图画
    while(l<=r && r<=n)
    {
        if(k==m)//判断是否符合要求
        {
            if(ans>r-l+1)   
            {
                ans=r-l+1;//ans记录最小区间长度
                ll=l; rr=r;
    //ll记录最小区间的左端点,rr记录最小区间的右端点
            }
            b[a[l]]--;
            if(b[a[l]]==0) k--;
            l++;
        }
        else{
            r++;
            b[a[r]]++;
            if(b[a[r]]==1) k++;
        }
    }

T3 小美的字符串变换

思路

本题是一个稍微变化一下的求解联通块的问题!

1利用循环枚举出矩形的形态!

for(int i = 1; i <= n; i ++){
        if(n % i == 0){
            int sum = 0;
            for(int j = 1; j <= n; j ++){
                if(vis[j] == 0){
                    dfs(j , n / i , a[j]);
                    sum ++;
                }
            }
            memset(vis , 0 , sizeof(vis));
            ans = min(ans , sum);
        }
    }

2利用dfs或者bfs对矩形进行联通块数量求解,直接染色即可!

void dfs(int x , int y , char z){
    if(z != a[x] || vis[x] == 1)
        return ;
    vis[x] = 1; 
    if(x + y <= n)
        dfs(x + y , y , z);
    if(x % y != 0)
        dfs(x + 1 , y , z);
    if(x % y != 1)
        dfs(x - 1 , y , z);
    if(x + y >= 1) 
        dfs(x - y , y , z);
    return ;
} 

本题是美团的算法岗位招人题目,还是考验大家对基础算法的理解和掌握!

T4 一道简答题

本题考察大家对上周线段树的掌握情况,当然这种区间修改,单点查询的题目,用树状数组也是很合适的!

考虑本题只会修改1和0,那么也就是我只需要打一个标记表示该区间的值是1或者0即可!

所以建树就不用了,只需要一个updata和push_down,query即可!

void push_down(int k)
{
    if(tag[k])
    {
        tag[k<<1]^=1;
        tag[k<<1|1]^=1;
        tag[k]=0;
    }
}
void updata(int k,int l,int r,int x,int y)
{
    if(x<=l&&r<=y)//如果当前区间完全被修改区间包含,整体xor
    {
        tag[k]^=1;
        return;
    }
    push_down(k);//下传标记
    int mid=(l+r)>>1;
    if(x<=mid)updata(k<<1,l,mid,x,y);
    if(mid<y)updata(k<<1|1,mid+1,r,x,y);
}
int query(int k,int l,int r,int x)
{
    if(l==r)return tag[k];
    push_down(k);//下传标记
    int mid=(l+r)>>1;
    if(x<=mid)return query(k<<1,l,mid,x);
    else return query(k<<1|1,mid+1,r,x);
}

思路2

树状数组其实和它思路是一样的,也是区间修改,然后下传即可!

下周大家可以跟随梓杰同学学习下树状数组,然后对比下两种结构的差别!

T5车站分级

思路

本题考察拓扑排序,主要是大家要学会思维转换!

难点

本题其实难点是建图,建图完成以后,就是正常的拓扑排序即可!

把每个车站看成图的一个点,如果从 a[1]到 a[s]中,停靠的站点为 a[1],a[2],…,a[s],则意味着

从 a[1]到 a[s]中没有停靠的车站的等级都小于 a[1],a[2],…,a[s]的等级,所以我们把这些站点分别 引一条有向边到 a[1],a[2],…,a[s]对应的点,比如样例 1 建立的图如下:

    for(int i=1;i<=m;i++) {
        memset(is,0,sizeof(is));//is表示是否是停靠站
        scanf("%d",&s);
        for(int j=1;j<=s;j++)
            scanf("%d",&st[j]),is[st[j]]=true;
        for(int j=st[1];j<=st[s];j++)
            if(!is[j])          //枚举站点,若不是已停靠的就小于所有停靠站的等级
                for(int k=1;k<=s;k++)   //枚举已停靠站点
                    if(!tuopu[j][st[k]]) tuopu[j][st[k]]=1,de[st[k]]++;//tuopu[i][j]表示j>i的级别,如上
    }

建图成功后,就是普通的拓扑排序了!

具体看代码注释

    for(int i=1;i<=m;i++) {
        memset(is,0,sizeof(is));//is表示是否是停靠站
        scanf("%d",&s);
        for(int j=1;j<=s;j++)
            scanf("%d",&st[j]),is[st[j]]=true;
        for(int j=st[1];j<=st[s];j++)
            if(!is[j])          //枚举站点,若不是已停靠的就小于所有停靠站的等级
                for(int k=1;k<=s;k++)   //枚举已停靠站点
                    if(!tuopu[j][st[k]]) tuopu[j][st[k]]=1,de[st[k]]++;//tuopu[i][j]表示j>i的级别,如上
    }

    do{
        top=0;
        for(int i=1;i<=n;i++)
            if(de[i]==0&&!bo[i]) {
                tt[++top]=i,bo[i]=true;//开始将出度为0的点删掉
            }
        for(int i=1;i<=top;i++)
            for(int j=1;j<=n;j++)
                if(tuopu[tt[i]][j]) tuopu[tt[i]][j]=0,de[j]--;//去边去点
        ans++;
    } while(top);

T6 公平分割

思路

其实就是一个背包问题,我们考虑最大容量下的最大价值,但是考虑天平是对半分的,那么我们通过循环,找到第一个过半的即可!


for(int i=1;i<=n;i++)
        for(int j=sum;j>=a[i];j--)
            dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
    for(int i=1;i<=sum;i++)
        if(dp[i]>=(sumb+1)/2){
            cout<<sum-i;
            break;
        } 
```