1. 程式人生 > 其它 >區間找最值的二分與字首和

區間找最值的二分與字首和

《1.題目一--最佳牛欄問題》

題目連結:https://www.acwing.com/problem/content/104/

用到的基本思想:二分,字首和,平均值的特殊處理

這個題目的題意是,給出N塊連續的田地,田地上有牛;
我們想要在這個給出的一系列田地中找出連續的田地(連續的意思是按照題目給出的如 1 2 3 4 5 6 72 3 4 是連續的,而1 3 5不是連續的)上牛數的最大平均值
1.由這裡N《=1e5,應該想到用二分找答案,然後去判斷這個二分出來的答案對不對;
於是題目變成了,我找到了答案mid,是否能夠找到一段平均值》=mid;
如果找到了那麼還有提升空間l=mid+1;
否則 r
=mid; 2.那麼如何去判斷這個最大平均值呢? 在一個區間上求值,第一個想到的是就是字首和; 但是這裡是平均值,有除數; 可以讓這題目給出的數值都-mid,然後轉化為字首和新得的,看某一區間是否》=0 如果》=0,那麼說明這一段區間》=mid; 3.當然這個是有長度要求的,即區間長度》=f; 我們可以開一個變數minv,一直維護其最小 由sum[k]-sum[minv]可以得到最大的
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace
std; 5 const int N = 1e5 + 10; 6 int n, f; 7 double sum[N]; 8 double num[N]; 9 bool check(double mid) 10 { 11 for (int i = 1; i <= n; i++) 12 sum[i] = sum[i - 1] + (num[i]-mid); 13 double minv=0; 14 for (int i=f;i<=n;i++) 15 { 16 minv=min(minv,sum[i-f]); 17 if
(sum[i]-minv>=0) return true; 18 } 19 return false; 20 } 21 int main() 22 { 23 scanf("%d%d", &n, &f); 24 for (int i = 1; i <= n; i++) 25 scanf("%lf", &num[i]); 26 double l = 0, r = 2000; 27 while (r - l > 1e-6) 28 { 29 double mid = (l + r) / 2; 30 if (check(mid)) 31 l = mid; 32 else 33 r = mid; 34 } 35 printf("%d",int(r*1000)); 36 return 0; 37 }

《2.題目2--防線》

題目連結:https://www.acwing.com/problem/content/description/122/
120. 防線
達達學習數學競賽的時候受盡了同仁們的鄙視,終於有一天......受盡屈辱的達達黑化成為了黑暗英雄怪獸達達。

就如同中二漫畫的情節一樣,怪獸達達打算毀掉這個世界。

數學競賽界的精英 lqr 打算阻止怪獸達達的陰謀,於是她集合了一支由數學競賽選手組成的超級行動隊。

由於隊員們個個都智商超群,很快,行動隊便來到了怪獸達達的黑暗城堡的下方。

但是,同樣強大的怪獸達達在城堡周圍佈置了一條“不可越過”的堅固防線。

防線由很多防具組成,這些防具分成了 N 組。

我們可以認為防線是一維的,那麼每一組防具都分佈在防線的某一段上,並且同一組防具是等距離排列的。

也就是說,我們可以用三個整數 S, E 和 D 來描述一組防具,即這一組防具佈置在防線的 S,S+D,S+2D,…,S+KD(K∈Z,S+KD≤E,S+(K+1)D>E)位置上。

黑化的怪獸達達設計的防線極其精良。

如果防線的某個位置有偶數個防具,那麼這個位置就是毫無破綻的(包括這個位置一個防具也沒有的情況,因為 0 也是偶數)。

只有有奇數個防具的位置有破綻,但是整條防線上也最多隻有一個位置有奇數個防具。

作為行動隊的隊長,lqr 要找到防線的破綻以策劃下一步的行動。

但是,由於防具的數量太多,她實在是不能看出哪裡有破綻。

作為 lqr 可以信任的學弟學妹們,你們要幫助她解決這個問題。

輸入格式
輸入檔案的第一行是一個整數 T,表示有 T 組互相獨立的測試資料。

每組資料的第一行是一個整數 N。

之後 N 行,每行三個整數 Si,Ei,Di,代表第 i 組防具的三個引數,資料用空格隔開。

輸出格式
對於每組測試資料,如果防線沒有破綻,即所有的位置都有偶數個防具,輸出一行 "There's no weakness."(不包含引號) 。

否則在一行內輸出兩個空格分隔的整數 P 和 C,表示在位置 P 有 C 個防具。當然 C 應該是一個奇數。

資料範圍
防具總數不多於108,

Si≤Ei,

1≤T≤5,

N≤200000,

0≤Si,Ei,Di≤2311
輸入樣例:
3
2
1 10 1
2 10 1
2
1 10 1 
1 10 1 
4
1 10 1 
4 4 1 
1 5 1 
6 10 1
輸出樣例:
1 1
There's no weakness.
4 3
思路是利用只有一個奇數,
1.只有一個奇數說明,我可以利用二分法l=0,r=0x3f3f3f3f;
去列舉在哪裡;
因為如果那個奇數在l-mid中,則l-mid這個區間的防具總和為奇數,
反之是奇數在mid+1-r;
2.如何求l-mid中的防具總和呢?
字首和
我們算0-mid與0-(l-1)的防具總和,相減為l-mid這個區間的防具總和;
如何求0-x中的防具總和呢?
遍歷每一組,注意我們是求0-x中的防具總和,是很容易的
for (int i=1;i<=n;i++){
if (x<si) ans+=0;
else ans+=(min(x,ei)-si)/di+1;
}
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 const int N = 1e5 + 10;
 6 struct node
 7 {
 8     int s, e, d;
 9 } zhu[N];
10 int t, n;
11 long long sum(int x)
12 {
13     long long ans = 0;
14     for (int i = 1; i <= n; i++)
15     {
16         if (x < zhu[i].s)
17             ans += 0;
18         else
19         {
20             ans += (min(x, zhu[i].e) - zhu[i].s) / zhu[i].d + 1;
21         }
22     }
23     return ans;
24 }
25 bool check(int l, int mid)
26 {
27     if ((sum(mid) - sum(l - 1)) % 2)
28         return true;
29     else
30         return false;
31 }
32 int main()
33 {
34     scanf("%d", &t);
35     while (t--)
36     {
37         scanf("%d", &n);
38         int l = 0, r = 1e9;
39         for (int i = 1; i <= n; i++)
40         {
41             scanf("%d%d%d", &zhu[i].s, &zhu[i].e, &zhu[i].d);
42             l = min(zhu[i].s, l);
43             r = max(zhu[i].e, r);
44         }
45         while (r > l)
46         {
47             int mid = (l + r) >> 1;
48             if (check(l, mid))
49                 r = mid;
50             else
51                 l = mid + 1;
52         }
53         long long ans = sum(r) - sum(r - 1);
54         if (ans % 2)
55         {
56             printf("%d %lld\n", r, ans);
57         }
58         else
59         {
60             printf("There's no weakness.\n");
61         }
62     }
63     return 0;
64 }這個程式碼有問題是過不了的,但是思路是對的