BalkanOI 2018 Parentrises(贪心+DP)

题意

https://loj.ac/problem/2713

思路

对于 \(\text{P1}\) 的档,首先可以看出 \(O(n^3)\) 的方法,即用 \(O(n^3)\)\(\text{DP}\) 判断合法性以及记录路径。具体是这样的,因为括号匹配可以用一个弹栈的模型去表示(前括号入,后括号弹),用一个整数就可以表示当前的匹配状态,所以用 \(dp_{i,j,k}\) 表示第 \(i\) 个括号,忽视蓝色括号栈中有 \(j\) 个前括号,忽视红色括号栈中有 \(k\) 个前括号。则如果加入一个红前括号,则 \(j\) 加一;若加入一个蓝前括号,则 \(k\) 加一;若加入一个绿前括号,则 \(j,k\) 均加一。后括号同理。

从这个转移来看,似乎就是一个走棋盘的模型,但是单看走棋盘似乎也看不出什么。那么把棋盘拍扁成一维,只保存当前位置到起点的距离。设 \((0,0)​\) 在最左上角,那么向右或下走距离加 \(1​\) ,右下则加 \(2​\) ,反之同理。不难发现,如果存在把距离变回零的方案,则棋盘上可以走回 \((0,0)​\) 的方案肯定可以构造出来。

一顿操作,问题变成了给定一个加减号序列,你需要在里面适当位置填上 \(1\)\(2\) ,满足任意前缀大于等于 \(0\) ,最终总和等于 \(0\)

这时候,贪心策略也渐渐显然,通过维护某一时刻最大的前缀 \(u\) ,最小的前缀 \(d\) 。扫到某一时刻,碰到加号(左括号)则 \(u+2,d+1\),碰到减号(右括号)则 \(u-1,d-2\) ,最大前缀小于零则要求一无法满足,而最小前缀小于零就补到零(表示可以将其中一个的 \(-2\) 变成了 \(-1\) ),最后得到的 \(d\) 不是零就说明加号过多,要求二无法满足,同样不合法。

我们得到的 \(u\) 就告诉我们如果只有 \(+2,-1\) 时,最后会是几,那我们就倒着扫这么多个数,把 \(+2\) 变成 \(+1\)\(-1\) 变成 \(-2\)

这样就得到了这个加减号序列,最后我们只用把 \(+1\) 换成红蓝交替的前括号,\(-1\) 换成红蓝交替的后括号,\(+2,-2\) 分别换成绿色前后括号即可。

\(O(n)\) 判断合法性,就直接按照这个 \(\text{DP}\) 就可以切 \(\text{P2}\) 档了,可以直接看代码。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
namespace Subtask1
{
    const int N=1e6+5;
    char str[N];
    int ans[N];
    int n;
    int check()
    {
        int d=0,u=0;
        FOR(i,1,n)
        {
            if(str[i]=='(')d+=1,u+=2;
            else
            {
                d-=2,u-=1;
                chk_max(d,0);
                if(u<0)return -1;
            }
        }
        if(d>0)return -1;
        return u;
    }
    void Solve()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s",str+1);
            n=strlen(str+1);
            int res=check();
            if(res==-1){printf("impossible\n");continue;}
            FOR(i,1,n)ans[i]=0;
            int flg=-1;
            if(res)DOR(i,n,1)
            {
                if(str[i]=='(')ans[i]=flg,flg=-flg;
                else ans[i]=2;
                res--;
                if(!res)break;
            }
            flg=-1;
            FOR(i,1,n)if(str[i]==')')
            {
                if(ans[i]==2)break;
                ans[i]=flg,flg=-flg;
            }
            FOR(i,1,n)
            {
                if(ans[i]==1)putchar('R');
                else if(ans[i]==-1)putchar('B');
                else putchar('G');
            }
            puts("");
        }
    }
};
namespace Subtask2
{
    const int P=1e9+7;
    const int N=305;
    int dp[N][N][N<<1];
    int ans[N];
    void pls(int &x,int y){x+=y;if(x>=P)x-=P;}
    void Solve()
    {
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        FOR(i,0,299)
        {
            FOR(j,0,i)FOR(k,0,2*i)
            {
                pls(dp[i+1][j+1][k+2],dp[i][j][k]);
                if(k)pls(dp[i+1][std::max(0,j-2)][k-1],dp[i][j][k]);
            }
            FOR(j,0,2*i)pls(ans[i+1],dp[i+1][0][j]);
        }
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n;
            scanf("%d",&n);
            printf("%d\n",ans[n]);
        }
    }
};

int main()
{
    int knd;
    scanf("%d",&knd);
    if(knd==1)Subtask1::Solve();
    else if(knd==2)Subtask2::Solve();
    return 0;
}
posted @ 2019-04-08 21:47 Paulliant 阅读(...) 评论(...) 编辑 收藏
友情链接: 曾经的csdn洛谷空间Little_Jianzryabcyyb系灬幼犬酱Angel_KittyNimphy
博主QQ号:2073146802   欢迎勾搭~