洛谷P1114 “非常男女”字首和+雙指標
阿新 • • 發佈:2021-06-24
# 字首和+雙指標
這道題第一眼看,大概就知道可能和字首和有關~
字首和可以清楚的表示男女之間連續的數量差的關係。**
也正是因為要用字首和,我們將女生的值定為-1,男生的值為1;
然後我們使用結構體來儲存每次輸入的資訊,結構體中一個值表示男女數,一個表示排序前的位置。然後對男女數量使用字首和。這樣尋找符合要求的區間的時候直接拍個序,再搜一下就好了。
struct Place{ int num,pos; };
有兩種情況表示是符合要求的[l,r]區間。
第一種:
當l位置的字首和的值和r位置的字首和的值相等時,說明此區間(l,r]內的男女人數一樣,所以一頓操作之後字首和的值沒有改變。
對於這種情況我們只需要對字首和的值排個序,然後每次找出“相同字首和內的最大區間長度”來更新我們的結果。
這裡我們可以使用雙指標演算法(其實也不完全是雙指標),即每次找到一個不同的值固定i指標,讓j指標往後移動來更新最長區間,當j指標的值和i指標不一樣的時候,i指標直接移動到j指標的當前位置,然後再繼續搜尋。這樣算下來,總共只需要掃一遍整個區間就能找出答案;
for(i=1; i<=n; i++) { for(j=i+1; j<=n&&arr[j].num==arr[i].num; j++) { res=max(res,arr[i].pos-arr[j].pos);if(arr[i].num==0) res=max(res,arr[i].pos); } i=j-1; }
第二種
(這一種我一開始漏了導致聽取wa聲一片)
當某個位置的字首和的值為0的時候說明從一開始到這個位置就是符合要求的區間,因此我們可以對於每個字首和是0的位置都更新一下最大區間。
當然,在寫排序函式的時候我們可以按照字首和的值從小到大排序,相同的值位置從大到小排序,這樣當掃到0的時候直接更新的一個就行了;
這是排序函式:
bool rule(Place A,Place B) { if(A.num!=B.num) return A.num<B.num; return A.pos>B.pos; }
下面附上AC程式碼:
#include<bits/stdc++.h> #define MAXN 100050 using namespace std; //結構體儲存字首和的值和排序前的位置 struct Place { int num,pos; }; //人數,arr來儲存,p是輸入的值,res最後的結果,i和j兩個搜尋的指標。 int n; Place arr[MAXN]; int p,res=0; int i,j; //按照字首和的值從小到大,相同的值按原來的位置從大到小排序 bool rule(Place A,Place B) { if(A.num!=B.num) return A.num<B.num; return A.pos>B.pos; } int main() { ios::sync_with_stdio(false); cin >> n; for(int i=1; i<=n; i++) { cin >> p; if(p==1) arr[i].num=1; //如果是男生就加上1 else arr[i].num=-1; //如果是女生就減去1 if(i!=1) arr[i].num+=arr[i-1].num; //進行字首和 arr[i].pos=i; //記錄原來的位置 } //按照規則排個序 sort(arr+1,arr+1+n,rule); //整個區間找一遍 for(i=1; i<=n; i++) { //兩個指標i表示每個值前面固定的指標,j表示往後查詢的指標; for(j=i+1; j<=n&&arr[j].num==arr[i].num; j++) { res=max(res,arr[i].pos-arr[j].pos); //當i和j位置的字首和值一樣,更新最大長度 if(arr[i].num==0) res=max(res,arr[i].pos); //特判,當此位置的字首和是0的時候,從頭到此位置就是最大長度,更新結果 } i=j-1; //當i和j的值不一樣的時候,將i挪到j的位置上,j繼續尋找 } cout << res; return 0; }