2016多校第4場 HDU 6076 Security Check DP,思維
阿新 • • 發佈:2017-08-08
pro begin sizeof || i++ 預處理 通過 第一個 ans
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6076
題意:現要檢查兩條隊伍,有兩種方式,一種是從兩條隊伍中任選一條檢查一個人,第二種是在每條隊伍中同時各檢查一個,前提是兩條隊伍中的兩個人的序號大於k。然後詢問檢查最少需要的時間。
解法:根據題意很容易想到dp[i][j]表示第一個隊伍已經檢查完前i個人,第二個人已經檢查完前j個人所需最小時間。但是這樣是O(n^2)毫無疑問會tle。我們發現k很小,所以我們可以對於兩種轉移方式分開處理。對於差值小於等於k的人可以普通的轉移。對於差值大於k的我們可以先預處理出來每個偏移量(第一隊列中選的人的位置和第二個隊列中選的人的位置的差值)差值小於等於k的點對,然後通過二分找到左邊最近的一個差值小於k的點,那麽中間按照這個偏移量一對一對的檢查,差值一定是大於k的。所以總復雜度O(n * k * log N)。
#include <bits/stdc++.h> using namespace std; const int maxn = 60010; int n, k, a[maxn], b[maxn], pos[maxn]; bool cmp(int x, int y){return x>y;} vector <int> v[maxn*2]; int dp[maxn][22]; int dfs(int x, int y){ if(!x||!y) return x+y; if(abs(a[x]-b[y])<=k){ int &g=dp[x][a[x]-b[y]+k]; if(g) return g; return g = min(dfs(x-1,y),dfs(x,y-1))+1; } auto it = lower_bound(v[x-y+n].begin(),v[x-y+n].end(),x,cmp); if(it == v[x-y+n].end()) return max(x, y); int t = *it; return dfs(t, y-x+t)+x-t; } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d %d", &n,&k); for(int i=0; i<=2*n; i++) v[i].clear(); memset(dp, 0, sizeof(dp)); for(int i=1; i<=n; i++){ scanf("%d", &a[i]); } for(int i=1; i<=n; i++){ scanf("%d", &b[i]); pos[b[i]] = i; } for(int i=n; i>=1; i--){ for(int j=a[i]-k; j<=a[i]+k; j++){ v[i-pos[j]+n].push_back(i); } } int ans = dfs(n, n); printf("%d\n", ans); } return 0; }
2016多校第4場 HDU 6076 Security Check DP,思維