1. 程式人生 > >Princess Principal(多括號匹配,區間合法查詢)

Princess Principal(多括號匹配,區間合法查詢)

ACM題集:https://blog.csdn.net/weixin_39778570/article/details/83187443
Princess Principal
題目:https://ac.nowcoder.com/acm/contest/201/J
題意:使用陣列代表括號,0,1為一對左右括號,1,2為另一對,3,4。。。同理。給定n個括號,查詢m次[l,r]範圍是否匹配合法。
解法:查詢次數比較多,使用一個數組記錄括號匹配情況,topp[i]表示從i開始往左邊第一個匹配不上的位置,那麼[L,r]區間是否匹配可以轉換為topp[L-1]是否等於topp[r],若相等則說明區間[l,r]可以完全匹配,因為topp[r]一定往左越過了L,括號是否匹配的話,依然用一個棧來維護,匹配上了就把左括號出棧,匹配不上就進棧,左括號,直接進棧。棧頂一定為當前位置往左第一個匹配不上的位置
解法二:仍然用棧來儲存,由於匹配方式是唯一的,我們用一個數組 b[i] 來記錄第 i個括號所匹配的括號的位置。若一個區間內的括號完全匹配,當我們從區間內取出一個單括號時,它所匹配的括號也必然在這個區間內,這種性質稱為“封閉性”。
然後我們預處理每個括號所匹配括號的位置即b[i],但是查詢的時候複雜度仍然是線性的。然而我們只需要對區間(l,r)求所匹配的括號位置的最值進行查詢即可,換言之,若區間(l,r)所匹配的括號的最大值或者最小值不在(l,r)裡,那麼就違反了“封閉性”。問題就轉換成了求區間最值,我們可以採用RMQ或線段樹解決。

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e6+5;
int n,m,q;
ll a[maxn],ttop[maxn];//a表示括號型別 
void pend(){
	stack<ll> st; // 存放下標 
	ttop[0] = 0;  // 初始狀態,可以理解為第0個不匹配 
	fo(i,1,n){ // 0,1  2,3  4,5 為不同括號(第0,1,2種) 
if(st.empty() || !(a[i]&1)) st.push(i); // 如果棧空,或者遇到左括號加入棧 else{ if(a[i] == a[st.top()]+1) st.pop(); // 右括號匹配上了左括號,去掉左括號 else st.push(i); // 匹配不上加(下標) 入棧 } if(st.empty()) ttop[i] = 0; // 棧空表示中間都能匹配,都是合法的 else ttop[i] = st.top(); // 匹配不上的情況下ttop[i]=i, 匹配上了ttop[i]=往左邊第一個匹配不上的下標
} } int main(){ scanf("%d%d%d",&n,&m,&q); fo(i,1,n) scanf("%lld",&a[i]); pend(); int l,r; while(q--){ scanf("%d%d",&l,&r); if(ttop[l-1]==ttop[r])puts("Yes"); // 表示第l-1個和第r個往左第一個匹配不上的下標相同 else puts("No"); // 如果相同則說明了r這個括號匹配不上的下標已經跳過了[l,r]這個範圍,說明[l,r]完全匹配 } }

解法二:ST演算法

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e6+5;
int n,m,q;
int a[maxn], b[maxn]; // b陣列儲存匹配上的括號的下標 
stack<int> st;
int dp_max[maxn][20],dp_min[maxn][20];  // 陣列大小maxn, log(n)/log(2)
// dp[i][j] 表示 [i,i+2^j -1] 的最值 
void ST(){
	fo(i,1,n){
		dp_max[i][0] = b[i];
		dp_min[i][0] = b[i];
	}
	int t = log(n)/log(2);
	fo(j,1,t){
		for(int i=1; i+(1<<j)-1<=n; i++){
			dp_max[i][j] = max(dp_max[i][j-1], dp_max[i+(1<<(j-1))][j-1]);
			dp_min[i][j] = min(dp_min[i][j-1], dp_min[i+(1<<(j-1))][j-1]); 
		}
	}
}
int RMQ_min(int l, int r){
	int k = log(r-l+1)/log(2);
	return min(dp_min[l][k], dp_min[r-(1<<k)+1][k]);
}
int RMQ_max(int l, int r){
	int k = log(r-l+1)/log(2);
	return max(dp_max[l][k], dp_max[r-(1<<k)+1][k]);
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	fo(i,1,n){
		scanf("%d",&a[i]);
		b[i] = -1;
		if(st.empty() || !(a[i]&1))st.push(i);
		else{
			if(a[i] == a[st.top()]+1){
				// 兩個位置都要匹配上 
				b[i] = st.top();
				b[st.top()] = i;
				st.pop();
			}else st.push(i); 
		}
	}
	ST();
	int L,R;
	fo(i,1,q){
		scanf("%d%d",&L,&R);
		if(RMQ_min(L,R)>=L && RMQ_max(L,R)<=R) puts("Yes");
		else puts("No");
	}
	return 0;
}