P6820 [PA2012]Two Cakes DP狀態優化
阿新 • • 發佈:2020-11-30
題意:
分析:
很久以前做過的題,在巨佬 fgf 的幫助下又學了一遍
- 暴力
直接設 DP 狀態 \(f[i][j]\) 表示第一個序列列舉到第 \(i\) 位,第二個序列列舉到第 \(j\) 位時的最短時間,按題意模擬就好了
- 正解
我們發現暴力做法中大部分的情況下 DP 都沒有做出任何決策直接轉移了,所以有好多狀態是無用的。所以我們只考慮 \(a[x]==b[y]\) 這種特殊狀態下的 DP,對於其他的情況直接轉移向 \(a[x]==b[y]\) 的情況
- 當 \(a[x]==b[y]\) 時
f[x]=min(dfs(x-1,y),dfs(x,y-1))+1;
因為一個 \(x\)
- 當 \(a[x]!=b[y]\) 時
我們需要找到之前他們剛好相等時的另一個點,我們注意到當轉移是不會影響兩個點的差值,所以我們直接對於每一類差值,存一下它們下一次相同時的點的位置,然後直接二分的查詢
複雜度 \(O(n \log n)\) , 因為決策點只有 \(n\) 個,每一次不同的情況都會 \(O(\log n)\) 的跳
程式碼:
#include<bits/stdc++.h> #define pii pair<int,int> #define mk(x,y) make_pair(x,y) #define lc rt<<1 #define rc rt<<1|1 #define pb push_back #define fir first #define sec second using namespace std; namespace zzc { inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } const int maxn = 1e6+5; int a[maxn],b[maxn],pos[maxn],f[maxn]; int n; vector<int> del[maxn<<1]; int get(int id,int pos) { int l=0,r=del[id].size()-1,mid,res=0; while(l<=r) { mid=(l+r)>>1; if(del[id][mid]<=pos) { res=del[id][mid]; l=mid+1; } else r=mid-1; } return res; } int dfs(int x,int y) { if(!x||!y) return x|y; if(a[x]==b[y]) { if(!f[x]) f[x]=min(dfs(x-1,y),dfs(x,y-1))+1; return f[x]; } int pre=get(x-y+n,x); return pre?dfs(pre,y+pre-x)+(x-pre):max(x,y); } void work() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) { b[i]=read(); pos[b[i]]=i; } for(int i=1;i<=n;i++) del[i-pos[a[i]]+n].pb(i); printf("%d\n",dfs(n,n)); } } int main() { zzc::work(); return 0; }