uoj#246. 【UER #7】套路(dp+分块?分类讨论?)
題目鏈接
分析:
目前為止我只能理解dp部分
我就喜歡這種單純不做作的題目
一看名字就明白了這道題的本質
中二的題目描述
很顯然,我們的關鍵就是求出最小相似度
樸素算法n^4
如果我們現在有一個權值數組
顯然,每一個數只可能與最鄰近ta的數產生貢獻
假設我們要求[i,j]之間的最小差距
那我們可以分成兩部分[i,k],[k+1,j]
枚舉k,取最小就可以了
但是這樣的復雜度是n^3
然而我們全然不用枚舉這個k
f[i][j]=abs(a[i]-a[j]) //i+1==j
f[i][j]=min{f[i+1][j],f[i][j-1],abs(a[i]-a[j])} //j-i>1
那還是n^2的,我們考慮能不能再次優化,去掉一維
空間:i的轉移只牽扯到i和i-1,所以可以滾粗動數組
更進一步,因為當前狀態i需要用到i+1的狀態,所以我們倒著推
每次讓當前的覆蓋上一次的,上式中f[i][j]需要用到f[i+1][j]的結果現在的話直接繼承
這樣我們就可以在空間上直接去掉一維
f[i]=min(f[i],f[i-1])
注意轉換成一維后f[i]表示的是終點在i的區間
最終算法:
實際上的基于dp的分類討論
如果一個區間的長度是x,那么最小差值一定不超過m/(x-1)
那么我們設一個常數s=sqrt(n)
當x < s時,用算法一中的算法求解。
當x>=s時,那么最小差不會超過 m/(s-1)
我們枚舉差值|z-x|<=m/(s-1) ,然后找到權值z最近一次出現的位置,然后計算答案
但是這樣還是不夠,我們必須保證兩個位置之間不存在再小的差值
所以我們還需要一個數組g[i]來記錄差值i最近一次出現的位置,
那么g[i]+1一定是在差值i+1或者更大的范圍內,所以用(posx-g[i]-1)*(i+1)來更新答案
詳盡題解
這里寫代碼片 #include<cstdio> #include<cstring> #include<iostream> #include<cmath> #define ll long longusing namespace std;const int N=200002; int n,m,k; int pos[N],g[N]; ll f[N]; ll a[N],ans=0;ll abs(ll x){if (x>0) return x; else return -x;}int main() {scanf("%d%d%d",&n,&m,&k);for (int i=1;i<=n;i++) scanf("%lld",&a[i]);int s=floor(sqrt(n));memset(f,127,sizeof(f)); //INFfor (int i=n;i>=1;i--){for (int j=i+1;j<=min(n,i+s-1);j++) //只做塊內的{f[j]=min(f[j],f[j-1]);f[j]=min(f[j],abs(a[j]-a[i]));if (j-i+1>=k) ans=max(ans,(ll)(j-i)*f[j]);} }for (int i=1;i<=n;i++){int t=m/s;for (int j=0;j<=t+1;j++){ll yy=a[i]+j;ll y=a[i]-j;if (j>=1) g[j]=max(g[j-1],g[j]);if (y>=1) g[j]=max(g[j],pos[y]);if (yy<=m) g[j]=max(g[j],pos[yy]);if (i-g[j]+1>max(s,k)) ans=max(ans,(ll)(i-g[j]-1)*(ll)(j+1));}pos[a[i]]=i;}printf("%lld\n",ans);return 0; }轉載于:https://www.cnblogs.com/wutongtong3117/p/7673197.html
總結
以上是生活随笔為你收集整理的uoj#246. 【UER #7】套路(dp+分块?分类讨论?)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天猫盒子android tv,天猫魔盒刷
- 下一篇: JAVA中常用的逻辑运算符_Java中的