题意
这道题被某大佬改编拿来出成考试题,是长这个样子的:
好的,其实这才是真正的题意:
给定初始序列和最终序列,每次选择一个数变成自己和相邻2个数的和。问初始序列是否可以变为最终序列,若可以,问最少需要多少次。
分析
发现这道题有很多种操作方式,就算是写搜索暴力都不是很好写。
正难则反,考虑从末状态到初状态,就是一直减去左右两边的数。
如果中间的数大于两边的数之和,那么中间那个数一定要被操作(设$a,b,c$分别为$i-1,i,i+1$上的$B$值)
而且在$b>a+c$条件不被破坏时,如果不对$b$操作,那么$a,c$也不会被操作(操作之后为负数,不合法)
只要$b>a+c$并且$Bi>Ai$ $b$就要一直减去$(a+c)$
由于是一定要减 是必要的 所以也最小
$b>a+c$的条件就是在if(...)-1 那个地方判断的 也可以写成if(!step) 就是判断操作次数至少为$1$次
如果不能操作(操作就成负数)那就不能有解 如果可以操作 下取整操作次数是一定要做的 哪怕只有一次都要做
做完之后 如果满足了$Ai==Bi$那最好 就ok啦
但如果不满足 根据step的算法 就是再减一次就变成负数 这个时候$b<a+c$了
就不一定需要操作 所以继续塞到队列里面等着有朝一日继续操作
用优先队列呢,也是因为"在$b>a+c$条件不被破坏时,如果不对$b$操作,那么$a,c$也不会被操作(操作之后为负数)"
如果不先对最大的那个数操作 那么后面也就没有办法操作。
基本上就是这样了。
不过还要注意开$long$ $long$(具体次数好像不是很好算,这种不好估计的情况还是都开起比较保险)
代码
1 #include<iostream> 2 #include<string> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<vector> 8 using namespace std; 9 #define N 200005 10 #define ll long long 11 #define fst first 12 #define snd second 13 //环:1:2,n n:n-1,1 14 int n,a[N],b[N]; 15 ll ans;//记得开ll 16 inline int rd() 17 { 18 int f=1,x=0;char c=getchar(); 19 while(c<'0'||'9'<c){if(c=='-')f=-f;c=getchar();} 20 while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); 21 return f*x; 22 } 23 priority_queue<pair<int,int> >Q; 24 //pair定义优先队列 先按first 再按second 自动排序 25 int main() 26 { 27 //freopen("hopeless.in","r",stdin); 28 //freopen("hopeless.out","w",stdout); 29 n=rd(); 30 for(int i=1;i<=n;i++) 31 a[i]=rd(); 32 for(int i=1;i<=n;i++) 33 { 34 b[i]=rd(); 35 if(a[i]!=b[i]) Q.push(make_pair(b[i],i)); 36 } 37 while(!Q.empty()) 38 { 39 pair<int,int> now=Q.top();Q.pop(); 40 int i=now.snd; 41 /*int pre=i-1,suf=i+1; 42 if(pre==0) pre=n; 43 if(suf==n+1) suf=1; 44 int step=(b[i]-a[i])/(b[pre]+b[suf]); 45 */ 46 int pre=(i+n-2)%n+1,suf=i%n+1; 47 int step=(b[i]-a[i])/(b[pre]+b[suf]); 48 if(b[i]-b[pre]-b[suf]<a[i]) 49 { 50 puts("-1"); 51 return 0; 52 } 53 ans+=step; 54 b[i]-=step*(b[pre]+b[suf]); 55 if(a[i]!=b[i]) Q.push(make_pair(b[i],i)); 56 } 57 printf("%lld ",ans); 58 return 0; 59 }