Usaco Training Section 5.1 Musical Themes
阿新 • • 發佈:2018-11-12
一長串數(<=5000個),若一段數和另一段數長度相同,且對應數的差相同,則算同一種。問最長的出現不止一次的序列的長度是多少。
法一(亂搞):
最直接的想法是列舉。我先把相鄰兩數的差存了下來,然後枚舉了起始點,又枚舉了與它相同的所有點,然後一位一位往後比對,這複雜度有點問題,但我判了一下如果迴圈次數超過一個值,就跳出,神奇地a了!!!
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define inf 2147483647 #define mp make_pair #define pii pair<int,int> #define pb push_back using namespace std; inline int read(){ int x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int a[5005],b[5005],pos[200],nxt[5005]; int main() { freopen("theme.in","r",stdin); freopen("theme.out","w",stdout); int n=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<n;++i){ b[i]=a[i]-a[i+1]+88; if(pos[b[i]]==0) pos[b[i]]=i; else nxt[pos[b[i]]]=i,pos[b[i]]=i; } int ans=0; ll tot=0; for(int i=1;i+3<n;++i){ int j=nxt[i]; if(j==0) continue; do{ int x=i,y=j; while(x+1<j-1&&y+1<n&&b[x+1]==b[y+1]) ++x,++y,++tot; if(x-i+2>=5&&x-i+2>ans) ans=x-i+2; j=nxt[j]; }while(j!=0); if(ans==n/2) break; ++tot; if(tot>181318066) break; } printf("%d\n",ans); }
法二(順推,dp思想):
法一肯定不是正解,於是我想了個比較靠譜的方法。
法一是可以優化的,如果我們確定了兩段之間的距離,是可以順推的。於是我們先列舉兩端間的距離i,再反著列舉第一段的起始位置j,如果a[j]-a[j+1]==a[j+i]-a[j+i+1],長度就+1,否則長度變為1。最後看答案是否>5。
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define inf 2147483647 #define mp make_pair #define pii pair<int,int> #define pb push_back using namespace std; inline int read(){ int x=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x; } int a[5005]; int main() { freopen("theme.in","r",stdin); freopen("theme.out","w",stdout); int n=read(),ans=0; for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<n;++i){ int l=1; for(int j=n-i-1;j;--j){ if(a[j]-a[j+1]==a[j+i]-a[j+i+1]){ ++l; if(l>i) l=i; if(l>ans) ans=l; } else l=1; } } printf("%d\n",ans>4?ans:0); }