自己思考的时候问题出在哪儿呢?还是没有挖掘和抓住问题的本质特征:要分成一段一段的,肯定是从一段一段的堆起来的过程过来的,如果能把这个过程还原,就可以判断出来是不是满足要求。一段的话,就有开头和结尾,然后开始找开头和结尾就可以了。自己的具有猜想性质的算法,如果不能及时证明,则应该换思路或者让队友去写样例试图证明,或者打样例表。
想出来差分之后,还有一个细节是如何判断3这个界限,其实有更简单的判断方法但是我这里写复杂了。但是整个题目重点还是在于逆向想到差分。
2019.7.10更新:今天翻到这个题发现自己以前写的是真诡异,写了个新的清爽版本。。。
新版
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll a[200015],b[200015]; int n,lst[200015]; int main() { int t,cs=0; scanf("%d",&t); while(t--){ cs++; printf("Case #%d: ",cs); scanf("%d",&n); ll jd=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } a[n+1]=0; for(int i=1;i<=n+1;i++) b[i]=a[i]-a[i-1],lst[i]=n+2,jd+=b[i]; if(jd){ puts("No"); continue; } lst[n+2]=n+10; for(int i=n+1;i>=1;i--){ if(b[i]<0) lst[i]=i; else lst[i]=lst[i+1]; } int np=lst[1],flag=1; for(int i=1;i<=n&&np<=n+1&&flag;i++){ if(b[i]>0){ while(b[i]){ if(np-i<3){ flag=0; break; } int cc=min(b[i],-b[np]); b[i]-=cc,b[np]+=cc; if(b[np]==0) np=lst[np+1]; } } } if(flag) puts("Yes"); else puts("No"); } return 0; }
旧版
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=2e5+50; const int mod=1e9+7; LL ori[maxn],diff[maxn]; int pps,ppy,pre; pair<int,int > ps[maxn],py[maxn]; int main() { int t,n,cs=0; scanf("%d",&t); while(t--) { cs++; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&ori[i]); } ori[n+1]=0; pps=ppy=0; pre=1; LL sum=0; int flag=1; for(int i=n+1;i>0;i--) { diff[i]=ori[i]-ori[i-1]; sum+=diff[i]; if(diff[i]>0) { int tp; for(tp=pre;tp<=ppy;tp++) { if(diff[i]&&py[pre].first-i<3) { flag=0; break; } if(diff[i]<=py[pre].second) { py[pre].second-=diff[i]; if(!py[pre].second) pre++; break; } else { diff[i]-=py[pre].second; pre++; } } if(diff[i]>0&&tp>ppy) { flag=0; break; } } if(diff[i]<0) { py[++ppy]=make_pair(i,-diff[i]); } } if(flag&&pre<=ppy) { flag=0; } if(flag) cout<<"Case #"<<cs<<": Yes\n"; else cout<<"Case #"<<cs<<": No\n"; } return 0; }