LightOJ - 1084 Winter(记忆化搜索)

题目大意:给出N个人的位置,每个人的最大移动距离是K。问如何移动,让人集中,才能使集中的地方数达到最小(集中的地方人数要大于等于三)

解题思路:因为所给的点是无序的,所以要先排序。
接着用dp[i]表示,以i开始,后面所有的人都按要求集中在一起,最少要集中几个地方
接着找到以i开始,距离小于等于i + 2 * k的那个最大数所在位置,判断一下这个集合有多少个人
如果这个集合里面的人超过超过三个,那么就可以将其中一个人分配出去了,以防下面集合缺人
如果超过4个,就可以分配两个出去
如果刚好是三个,那就不用分配出去了
最多分配出去两个,如果大于等于三个了,就相当于把一个集合分成两个了,不符合题意

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
const int INF = 0x3f3f3f3f;

int val[N], dp[N];
int n, k, cas = 1;

void init() {
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++)
        scanf("%d", &val[i]);
    sort(val, val + n);
}

int find(int num) {
    int l = 0, r = n - 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (val[mid] <= num) l = mid + 1;
        else r = mid - 1;
    }
    return l;
}

int dfs(int cur) {
    if (cur >= n) return 0;
    if (~dp[cur]) return dp[cur];
    int Min = INF;
    int pos = find(val[cur] + 2 * k);

    //刚好三个人
    if (pos - cur >= 3) {
        Min = min(Min, 1 + dfs(pos));
    }
    //超过三个人,分配一个出去
    if (pos - cur >= 4) {
        Min = min(Min, 1 + dfs(pos - 1));
    }
    //超过4个人,分配两个出去
    if (pos - cur >= 5) {
        Min = min(Min, 1 + dfs(pos - 2));
    }
    return dp[cur] = Min;
}

void solve() {
    memset(dp, -1, sizeof(dp));
    dfs(0);
    if (dp[0] == INF) 
        printf("Case %d: -1\n", cas++);
    else 
        printf("Case %d: %d\n", cas++, dp[0]);
}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值