hdu6076

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) ;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值