2021牛客多校#10 F-Train Wreck(数学,优先队列)

本文探讨了一种算法问题,如何为一个只有一条死胡同轨道的栈状火车站分配彩色火车,确保每次入栈时栈内颜色序列独特。通过转化为树形结构,利用优先队列策略,博主展示了如何确定颜色分配,最终提供了C++代码实现。
摘要由CSDN通过智能技术生成

题目链接

传送门

题目大意

有一个旧火车站,其只有一个死胡同轨道,可以视为一个栈,可以将火车入栈或者出栈。有 n ( 1 ≤ n ≤ 1 0 6 ) n(1\leq n\leq 10^6) n(1n106)彩色辆火车进站,第 i i i 辆火车的颜色是 k i ( 1 ≤ k i ≤ n ) k_i(1\leq k_i\leq n) ki(1kin),进出栈顺序已定,现在你需要为每次入栈操作分配一辆火车,使得入栈时栈中其他火车颜色序列唯一。

题解

题目的要求就是在每次输入左括号的序列不能与之前的序列相同。
如果用数字表示输入左括号时的长度大小,当前的我们可以将样例 ( ) ( ( ) ) ( ) ()(())() ()(())()变为 1121 1121 1121,可得我们需要保证三个1长度的颜色不相同。
再如 ( ( ) ( ) ( ) ) (()()()) (()()()),为 1222 1222 1222,我们需要保证三个2代表的其中第二个数字不相同,再观察一下图形,我们可以得出括号内相同层次的所代表的颜色不能相同,而之前的颜色是相同的。
你想到了什么?树的序列
在这里插入图片描述

通过举例,我们发现可以将其变为一棵树,要求是某个节点的所有子节点颜色不能相同。
只要颜色数满足所有非叶节点的子节点数,那么它就是可行的。
对于每个子节点,我们只需要优先选择颜色多的染色即可。
总结:
①我们需要建一棵树,表示其括号从属情况;
这里提供一种方法,对于每一个左括号,继续往右,若遇到长度小于等于它的左括号,
就起码遇到了一个右括号,跳出,不然,则是其子节点。
②对于每一个子节点,我们用优先队列选取较大的进行染色;
一起取出 − 1 -1 1再一起放入,
如果染色的数量不够了,则输出“NO“。

参考代码

#include<bits/stdc++.h>
#define FOR(i,n,m) for(int i=n;i<=m;i++)
using namespace std;
void read(int &x)
{
    int ret=0;
    char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
        ret=ret*10+c-'0',c=getchar();
    x=ret;
}
const int N=1e6+5;
struct node{
    int id,num;
}co[N];
priority_queue<pair<int,int>> q;
vector<int> v[N];
int n,t,t1;
int siz1[N],siz2[N],f[N],c[N];
int st[N],tt;
int s1=0;
void dfs(int x)
{
    if(x==n)
        return;
    if(s1==n)
        return;
    FOR(i,s1+1,n)
    {
        if(siz1[i]<=siz1[x])      //不为其子节点
            return;
        s1=i;
        v[x].push_back(i);       //构树
        dfs(i);
        i=s1;                //下一个
    }
}
vector<pair<int,int>> va;
int check(int x)
{
    if(v[x].size()==0)
        return 1;
    int s=0;
    while(s<v[x].size())        //染色(一起放出)
    {
        if(q.empty())
            return 0;
        va.push_back(q.top());       //优先选择栈顶颜色多的
        q.pop();
        va[s].first--;
        if(va[s].first<0)
            return 0;
        c[v[x][s]]=va[s].second;
        s++;
    }
    s=0;
    while(s<v[x].size())          //重新放入
        q.push(va[s++]);
    va.clear();
    FOR(i,0,v[x].size()-1)
        if(check(v[x][i])==0)
            return 0;
    return 1;
}
int main()
{
    read(n);
    FOR(i,1,2*n)
    {
        char c;
        cin>>c;
        if(c=='(')
            siz1[++t]=++t1;
        else
            t1--;
    }
    FOR(i,1,n)
    {
    	co[i].id=i;
    	co[i].num=0;
    }
    FOR(i,1,n)
    {
        if(siz1[i]==1)
        {
            s1=i;
            dfs(i);
            v[0].push_back(i);      //对于长度为1的时候,我们设其父节点为0
            i=s1;                  //下一个
        }
    }
    FOR(i,1,n)
    {
        int x;
        read(x);
        co[x].num++;              //统计颜色数量
    }
//     FOR(i,0,n)
//         for(int j=0;j<v[i].size();j++)
//             cout<<i<<" "<<v[i][j]<<endl;
    FOR(i,1,n)
    	q.push(make_pair(co[i].num,co[i].id));
    if(check(0)==0)
        puts("NO");
    else
    {
        puts("YES");
        FOR(i,1,n)
            printf("%d ",c[i]);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值