標籤:方式 bre eof log max namespace poj false 而且
轉自http://www.myexception.cn/program/1839999.html
妹子滿分~~~~
毛毛蟲演算法——尺取法
有這麼一類問題,需要在給的一組資料中找到不大於某一個上限的“最優連續子序列”
於是就有了這樣一種方法,找這個子序列的過程很像毛毛蟲爬行方式,我管它叫毛毛蟲演算法,比較流行的叫法是“尺取法”。
喏,就像圖裡的妹紙一樣~
還是舉個栗子:
Poj3061
給長度為n的數組和一個整數m,求總和不小於m的連續子序列的最小長度
輸入
n = 10,m = 15
5 1 3 5 10 7 4 9 2 8
輸出
2
那麼我們先用sum存當前這個子序列的和,從左邊第一個數來存,直到這個子序列的和大於等於m為止,再記錄下當前長度。
其實相當於當不滿足條件就入隊,然後得到隊列長度,再將隊首元素出隊,再進行下一次的入隊,直到滿足條件再次出隊,並且將這一次的長度與曆史最短長度進行取捨,最後掃到最後的元素卻無法再滿足入隊條件的時候就結束,此時用O(n)的時間就可以得到答案。
如下,我把範例用毛毛蟲爬一遍,底線的是當前“毛毛蟲著地”也就是剛好滿足題意的子序列的地方:
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
祖傳代碼。。。
C++
/************************************************************************* > File Name: main.cpp > Author: haoran > Created Time: 2015年01月19日 星期一 21時04分36秒 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <fstream>#include <algorithm>using namespace std;int a[200000];int main(){// freopen("in.txt","r",stdin); ios::sync_with_stdio(false); cin.tie(false); int n,max,sum,T; while(cin>>T) { while(T--) { cin>>n>>max; for(int i = 0 ; i < n ; i++) cin>>a[i]; int i = 0,j = 0,sum = 0,ans = n+1; while(1) { while(j < n && sum <= max) sum += a[j++]; if(sum < max) break; ans = min(j-i,ans); sum -= a[i++]; } if(ans > n) ans = 0; printf("%d\n",ans); } } return 0;}
java大法的
import java.util.Scanner;public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Scanner cin = new Scanner(System.in); int a[] = new int[100010]; int T = cin.nextInt(); for(int ii = 0 ; ii < T ; ii++) { int n = cin.nextInt(); int m = cin.nextInt(); for(int i = 0 ; i < n ; i++) a[i] = cin.nextInt(); int l = 0,r = 0,ans = m+1,sum = 0; while(true) { while(r < n && sum < m) sum += a[r++]; if(sum < m) break; ans = min(ans,r-l); sum -= a[l++]; } if(ans > n) ans = 0; System.out.println(ans); } } private static int min(int ans, int sum) { // TODO Auto-generated method stub return ans < sum ? ans : sum; }}
買一送一,再給大家一顆栗子;
Poj3320
這道題費點腦子,大意:
一個考前一天看一整本書發獃的學渣,找學霸劃知識點,學霸告訴他每一頁的知識點(每一頁只有一個知識點而且頁與頁之間可以重複知識點!),每一個知識點都用一個數字表示,給你這本書一共有n頁厚,學渣很懶,只想讀連續若干頁的書,還不想看太多頁,所以要你幫他找覆蓋所有出現過的知識點的連續頁的頁數最薄有幾頁。
輸入
n = 5
1 8 8 8 1
輸出
2
很顯然一共有5頁書,卻只有2個知識點,想讀就讀前兩頁,所以就輸出2頁。先想想需要解決什麼問題:
1.要解決出現一共多少個知識點
2.根據當前子序列包含的知識點數來入隊、出隊
其實就這樣。
第一個問題其實可以用set來解決,知識點數量就是set的size大小。
下一個問題其實可以用map來存,存每一頁出現的知識點在這個子序列中出現的次數,如果是0的話就把這個知識點放進來並++,直到所有知識點覆蓋為止。
在所有知識點覆蓋的基礎上出隊,直到某一個知識點在子序列的出現次數為0的時候,再從後面入隊,並在這些操作中記錄下最少頁數就可以了,剩下的和第一顆栗子一個味道。
祖傳C++代碼如下~
#include <iostream>#include <map>#include <set>#include <cstdio>#include <algorithm>#include <cstdlib>#include <cstring>using namespace std;int n,m,a[1000010];void solve(){ set<int> all ; map<int,int>cnt; for(int i = 0 ; i < n ; i++) { scanf("%d",&a[i]); all.insert(a[i]); } m = all.size(); int l = 0 , r = 0 , ans = n+1,sum = 0; while(1) { while(r < n && sum < m) if(cnt[ a[r++] ]++ == 0) sum++; if(sum < m)break; ans = min(ans,r-l); if(--cnt[ a[l++] ] == 0) sum--; } printf("%d\n",ans);}int main(){ #ifdef H_R freopen("in.txt","r",stdin); #endif // H_R while(scanf("%d",&n)!=EOF) { solve(); } return 0;}
【轉】毛蟲演算法——尺取法