zoukankan      html  css  js  c++  java
  • BalkanOI 2018 Parentrises(贪心+DP)

    题意

    https://loj.ac/problem/2713

    思路

    对于 ( ext{P1}) 的档,首先可以看出 (O(n^3)) 的方法,即用 (O(n^3))( ext{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)) 判断合法性,就直接按照这个 ( ext{DP}) 就可以切 ( ext{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
    ");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
    ",ans[n]);
    		}
    	}
    };
    
    int main()
    {
    	int knd;
    	scanf("%d",&knd);
    	if(knd==1)Subtask1::Solve();
    	else if(knd==2)Subtask2::Solve();
    	return 0;
    }
    
  • 相关阅读:
    MUSIC分辨率与克拉美罗下界的关系
    EXCEL 基本函数
    新手如何正确的开始练车
    5.20考试整理
    树上倍增 x
    逆元 x
    BSGS ! x
    【テンプレート】LCA
    [HDOJ5783]Divide the Sequence(贪心)
    [HDOJ5791]Two(DP)
  • 原文地址:https://www.cnblogs.com/Paulliant/p/10673704.html
Copyright © 2011-2022 走看看