一道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]={0}; 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 Ball http://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 }
2.POJ -3263 Tallest Cow http://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 }
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 }