Security Check(多校第四场)
题意是有两队人在排队等待安检,每个人有一个权值,每分钟可以检查一个人的,当两个人权值差的绝对值>k时,可以一分钟同时检查这两个人。问把所有人检查完所需要的最短时间。n<=60000,k<=10,Ai,Bi均是(1-n)的全排列。
如果这个n比较小我们容易得到一个f[i][j]表示检查完第一队前i个人,第二队前j个人所需要的最短时间。
if |A[i] - B[j] <= k| f[i][j] = min(f[i-1][j] , f[i][j-1]) + 1;
else
f[i][j] = f[i-1][j-1] + 1;
显然这道题里时间和空间都完全接受不了。
但是观察Ai,Bi数字的情况以及k的范围可以发现,对于第一种情况实际上只有不超过n*2k种。我们可以先把这种情况预处理出来,
对于满足情况1的f[i][j],我们要把f[i-1][j] 和f[i][j-1]算出来,考虑f[i-1][j],如果满足情况1,那么这个值应该是已经被算过的,直接取出来用即可,如果是情况2,我们应该向前去找,直到遇到第一个[x][y],满足|A[x] - B[y] |<=k&& (x-y) == (i-1 - y) ,f[i][j-1] 也可同理计算得。怎么找呢,显然不能一个一个往前面去找,我们可以把(i - j)存起来,每处理一次就把(i-j)更新了,假设当前处理到第i个,那么前i-1个都被更新过,我们找一下最新的(i-j)里 存的东西,就是我们要找的那个x , y = x + j - i。
由于这个j是离散的不是连续的,我一开始用map实现,但是可能常数的原因运行时间很长。然后可以用vector做,复杂度大概是n*k*k 。
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define LONG long long
const int INF=0x3f3f3f3f;
const LONG MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
int dp[61000][25] ;
vector <int > vec[61000] ;
int A[61000] ,B[61000] ;
int take[121000];
int calcu(int a , int b )
{
if(take[a-b+60001] == 0)
if( b< a)return a;
else return b ;
int p ,len = vec[take[a-b+60001]].size() ;
for(int i = 0 ; i < len ; ++ i)
if(vec[take[a-b+60001]][i] == take[a- b+60001] + b - a)
{
p = i ; break ;
}
return dp[take[a-b+60001]][p] + a - take[a-b+60001] ;
}
int C[60010] ;
int main()
{
// freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
//freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","w",stdout);
int T;
cin >> T ;
int n , K ;
while(T --)
{
cin >> n >>K ;
clr1(dp) ;
clr0(take) ;
for(int i = 1;i <= n ; ++ i )scanf("%d",&A[i]) ,C[A[i]] = i ;
for(int i = 1;i <= n ; ++ i )scanf("%d",&B[i]) ,vec[i].clear() ;
for(int i = 1;i <= n ; ++ i)
for(int j = max(B[i] - K ,1); j<=min( n ,B[i] + K ); ++j)
vec[C[j]].push_back(i) ;
for(int tt = 1; tt<= n ;++tt)
{
int i = tt ;
int len = vec[i].size() ;
for(int j = 0 ; j < len ; ++ j )
{
int it = vec[i][j] ;
int x = tt , y = it ;
int ret1 = INF , ret2 = INF ;
int judge1 = 0 ,judge2 = 0 ;
int p1 , p2 ,len1 = vec[i-1].size() ;
///////////////////////////////////
for(int k = 0 ;k < len1; ++ k)
if(vec[i-1][k] == y)
{
p1 = k ;
judge1 = 1 ; break ;
}
if(! judge1 )
ret1 = calcu(x-1 , y) ;
else ret1 = dp[x-1][p1] ;
///////////////////////////////
///////////////////////////////
for(int k = 0 ;k < len ; ++ k)
if(vec[i][k] == y - 1)
{
p2 = k ; judge2 = 1; break ;
}
if(!judge2)
ret2 = calcu(x , y - 1) ;
else ret2 = dp[x][p2] ;
dp[i][j] = min(ret1 , ret2 ) + 1;
}
for(int j = 0 ; j < len ; ++ j)
take[i - (vec[i][j])+60001] = i ;
}
int ans ;
if(vec[n][vec[n].size() - 1] != n )
ans = calcu(n - 1 , n - 1) +1;
else ans = dp[n][vec[n].size()- 1] ;
// for(int i = 0; i <= n ;++ i)
// {
// for(int j = 0 ;j < vec[i].size() ;++ j)printf("%d ",dp[i][j]) ;cout<<endl ;
// }
printf("%d\n",ans) ;
}
}