一道codeforces題目引發的差分學習
題目:B. Suffix Operations
題意:給定一個長為n的陣列a,你可以進行兩種操作:1).字尾+1; 2)字尾-1; 問需要最少多少步操作你才能使得陣列中元素全部相等,並且首先你可以改變其中任何一個元素成為任何一個數,並且不被計入步數
思路:
首先來看一下陣列中大致分為這三種情況:舉例
3 5 7 (上升式)這樣中間的5變成3-7之間的任何一個數都能最終變成3
7 5 3 (下降式)與上述相同
7 3 5 (斷崖式)最終要讓他變成7,會產生浮動變化
通過上面的例子,就想到會用到差分陣列(d[n]),總共的浮動次數是不變的,只需要統計如果一個數變化的時候影響最大的次數是多少,因為最終肯定會是出現階段性的後面的數要變成它前面的數,所以要分情況討論d[i]變化引起的d[i+1]的變化,具體看程式碼
程式碼:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<map> 7 #include<queue> 8 using namespace std; 9 const int maxx=2e5+1;//14.12 10 //找差分 11 long long int a[maxx]={0}; 12 long long int d[maxx]={0View Code}; 13 int main(){ 14 int n; 15 scanf("%d",&n); 16 while(n--){ 17 int m; 18 scanf("%d",&m); 19 20 long long int sum=0; 21 for(int i=1;i<=m;i++){ 22 scanf("%lld",&a[i]); 23 d[i]=a[i]-a[i-1];//差分陣列 24 if(i>1){25 26 sum+=abs(d[i]); 27 } 28 } 29 30 long long int maxn=0; 31 for(int i=1;i<=m;i++){ 32 long long int x=abs(d[i]),y=abs(d[i+1]); 33 if(i==1){ 34 maxn=max(maxn,abs(d[2])); 35 }else if(i==m){ 36 maxn=max(maxn,abs(d[m])); 37 }else{ 38 long long int y1; 39 if(d[i]>0){ 40 y1=d[i+1]+x; 41 }else{ 42 y1=d[i+1]-x; 43 } 44 maxn=max(maxn,x+y-abs(y1)); 45 } 46 } 47 sum-=maxn; 48 printf("%lld\n",sum); 49 50 51 } 52 }
差分陣列/字首和的應用:
主要就是對陣列中的數字進行操作,通過小區間波動改變一整個陣列
1.HDU-1556 Color the Ballhttp://acm.hdu.edu.cn/showproblem.php?pid=1556
思路:一開始以為直接用a[maxx]陣列進行遍歷迴圈就行,結果TLE掉了,時間複雜度是O(n2),結果看了看並沒有這麼簡單,應該是用兩個陣列,一個儲存實際值(顯然初始值都是0)c[n],一個儲存浮動變化d[n]
d:0 1 0 0 -1
c:0 1 1 1 0
這樣看來也有一些逆向字首和的想法,儲存的時候先儲存d[i],然後按照字首和的想法進行輸出,這樣時間複雜度就是O(n)
程式碼:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<map> 7 #include<queue> 8 using namespace std; 9 const int maxx=2e5+1;//14.12 10 int main(){ 11 int n; 12 while(~scanf("%d",&n)){ 13 int a[maxx]={0}; 14 if(n==0){ 15 break; 16 } 17 int c[maxx]={0}; 18 for(int i=1;i<=n;i++){ 19 int t,b; 20 scanf("%d %d",&t,&b); 21 c[t]++; 22 c[b+1]--; 23 } 24 int sum=0; 25 for(int i=1;i<=n;i++){ 26 sum+=c[i]; 27 printf("%d",sum); 28 if(i<n){ 29 printf(" "); 30 } 31 } 32 printf("\n"); 33 } 34 }View Code
2.POJ -3263 Tallest Cowhttp://poj.org/problem?id=3263
思路:一開始想著用陣列的加加減減,還是所有的從0開始,看他們的相對大小就好了,結果發現做的時候兩邊端點都加加,中間的都減減,這樣出來的結果就是錯的,後來發現差分陣列想錯了,並不是這樣,而且這樣也會TLE掉,這個題目中問的是最高多少,而且同時加減這樣端點和中間就會差2而不是1,所以讓所有的牛最初始的高度是h,然後再建立一個數組b[n]儲存差分值,x,y為區間端點,b[x+1]--,b[y]++,而且這個題還需要區間判重一下,見程式碼
程式碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 #define ll long long 8 #define mem(a,b) memset(a,b,sizeof(a)) 9 using namespace std; 10 const int inf=0x3f3f3f3f; 11 const int mm=1e4+10; 12 int a[mm],b[mm]; 13 int vis[mm][mm]; 14 int main(){ 15 int n,pos,h,r; 16 scanf("%d%d%d%d",&n,&pos,&h,&r); 17 for(int i=0;i<=h+1;i++) 18 a[i]=0; 19 int x,y; 20 for(int i=1;i<=r;i++){ 21 scanf("%d%d",&x,&y); 22 if(x>y) swap(x,y);// 23 if(vis[x][y])//判重 24 continue; 25 vis[x][y]=1; 26 b[x+1]--;//後面的減少 27 b[y]++;//前面的增加 28 } 29 for(int i=1;i<=n;i++){ 30 a[i]=a[i-1]+b[i]; 31 printf("%d ",a[i]); 32 } 33 return 0; 34 }View Code
TLE掉的程式碼:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdio> 6 using namespace std; 7 const int maxx=1e5+10; 8 int main(){ 9 int n,ii,h,r; 10 cin>>n>>ii>>h>>r; 11 int a[maxx]={0}; 12 for(int i=1;i<=n;i++){ 13 a[i]=h; 14 } 15 for(int i=0;i<r;i++){ 16 int num1,num2; 17 scanf("%d %d",&num1,&num2); 18 if(num1>num2){ 19 swap(num1,num2); 20 } 21 for(int j=num1+1;j<num2;j++){ 22 a[j]--; 23 } 24 } 25 for(int i=1;i<=n;i++){ 26 printf("%d ",a[i]); 27 } 28 }View Code