1. 程式人生 > 其它 >洛谷P1114 “非常男女”字首和+雙指標

洛谷P1114 “非常男女”字首和+雙指標

# 字首和+雙指標

這道題第一眼看,大概就知道可能和字首和有關~

字首和可以清楚的表示男女之間連續的數量差的關係。**

也正是因為要用字首和,我們將女生的值定為-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;
}