HDU 6076 Security Check (DP, 2017 Multi-Univ Training Contest 4)

29 篇文章 0 订阅
14 篇文章 0 订阅

Problem

两列队伍,每队 N 人,A 队每人 rank 为 ai ,B 队每人 rank 为 bi ,要求从队首向队尾顺次检查(完成检查的即离队),每次检查可以是下列两种操作的任一种:

  1. 任选一个队伍的队首检查(如果该队非空)
  2. 如果两队队首 aibi>K ,则可以同时检查两队队首。

Limit

1N60000

1k10

1ai,biN

aiaj, bibj

Idea

正常的 dp 思路是:

dp[i][j] 表示 A 队检查了前 i 个人,B 队检查了前 j 个人的最少检查次数。时空复杂度都将达到 O(N2)

将上述 dp 思路在二维坐标看思考(i 为 x 轴,j 为 y 轴)。

  • 则操作 1 等同于 dp[i][j]=min(dp[i][j-1], dp[i-1][j])+1 ,即从正下方点或左点转移。
  • 操作 2 等同于 dp[i][j]=min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1 = dp[i-1][j-1]+1 (其中左下点一定小于等于左点与下点),即从左下点转移。 继续深化操作 2 ,dp[i][j] = dp[i-1][j-1]+1 = dp[i-2][j-2]+2 = dp[i-3][j-3]+3 ... ,当点 (i, j) ,(i-1, j-1), (i-2, j-2) … 能够满足操作 2 的转移条件时,可快速跳转,无须中间过程。

由于 K 值较小,故操作 1 的状态有限,上限在 NK ,故利用记忆化搜索,将 dp[n][n] 递归转移,若当前 (i, j) 不满足操作 2 的条件,则暴力判断从正下点或左点转移(最多 O(NK) )。若满足条件,则找到最大的不满足操作 2 的 (i-k, j-k) ,转移为 dp[i][j] = dp[i-k][j-k]+k 。对于这部分的查找,由于 sub = i-j = (i-k)-(j-k) ,故预先将不满足条件的组合按照 i-j 的差值存储,二分查找对应差值的最大小于 (i, j) 的点。

思路来源

Code

#include<bits/stdc++.h>
using namespace std;
const int N = 60000 + 10;
const int inf = 1e9 + 7;
const int SUBSTRACT = 2*N;
int T, n, k, a[N], b[N], posb[N];
vector<int> v[SUBSTRACT];
map<int, int> dp[N];
bool cmp(int x, int y) {
    return x > y;
}
void init() {
    for(int i=0;i<=2*n;i++)
        v[i].clear();
    for(int i=0;i<=n;i++)   dp[i].clear();
}
int dfs(int x, int y) {
    if(!x || !y)    return x+y;
    if(dp[x].find(y) != dp[x].end())    return dp[x][y];
    if(abs(a[x] - b[y]) <= k) {
        dp[x][y] = min(dfs(x-1, y), dfs(x, y-1)) + 1;
    } else {
        int pos = lower_bound(v[x-y+n].begin(), v[x-y+n].end(), x, cmp) - v[x-y+n].begin();
        if(pos >= v[x-y+n].size())  return max(x, y);
        dp[x][y] = dfs(v[x-y+n][pos], v[x-y+n][pos]-x+y) + x-v[x-y+n][pos];
    }
    return dp[x][y];
}
int main()
{
    scanf("%d", &T);
    while(T-- && scanf("%d %d", &n, &k)!=EOF)
    {
        init();
        for(int i=1;i<=n;i++)
            scanf("%d", &a[i]);
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &b[i]);
            posb[ b[i] ] = i;
        }

        for(int i=n;i;i--)
        for(int gap=0;gap<=k;gap++)
        {
            if(a[i]-gap > 0)    v[ i - posb[ a[i]-gap ] + n ].push_back(i);
            if(gap && a[i]+gap <= n)    v[ i - posb[ a[i]+gap ] + n ].push_back(i);
        }
        printf("%d\n", dfs(n, n));

    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值