牛客小白月赛99:自爆机器人(完全背包)

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

在一条水平线上存在着一个自爆机器人和一只怪物。自爆机器人初始坐标为 0\mathrm{0}0 ,怪物坐标为 n\mathrm{n}n 。自爆机器人在启动后将向怪物以 1\mathrm{1}1 坐标每秒的速度移动,并在遇到怪物的瞬间爆炸,造成大小等同于行动时间的伤害。

你是这款游戏的玩家,在机器人移动过程中你可以在给定的 m\mathrm{m}m 个不同整数坐标上任意建造或摧毁墙壁(同一坐标可以重复建造、摧毁墙壁)。如果机器人在移动过程中撞到墙壁,其会向反方向移动。

当然,为了防止玩家无限制的增加爆炸伤害,游戏开发者还给自爆机器人设置了最大起爆时间 t\mathrm{t}t 。一旦耗时达到 t\mathrm{t}t 机器人会立刻自爆。机器人只有在与怪物位于同一坐标时引爆才会对其造成伤害,现在请你最大化自爆机器人对怪物造成的伤害。

输入描述:

 

第一行给出一个整数 T(1≤T≤104)\mathrm{T}(\mathrm{1 \le T \le 10^4})T(1≤T≤104) ,表示数据组数。

对于每组测试数据:

第一行给出三个整数 n,m(1≤m<n≤2×105)\mathrm{n,m}(\mathrm{1 \le m < n \le 2\times10^5})n,m(1≤m<n≤2×105),t(1≤t≤2×105)\mathrm{t}(\mathrm{1 \le t \le 2 \times 10^5})t(1≤t≤2×105),表示坐标数,可建造或摧毁墙壁的坐标数,以及最大起爆时间。

第二行给出 m\mathrm{m}m 个不同整数,表示可建造、摧毁墙壁的坐标 xi(1≤xi<n)\mathrm{x_i}(\mathrm{1 \le x_i < n})xi​(1≤xi​<n)。

保证所有测试数据中  ∑n≤2×105,∑t≤2×105\mathrm{\sum n \le 2\times10^5,\sum t \le 2\times10^5}∑n≤2×105,∑t≤2×105

输出描述:

每组测试数据输出一个整数,为可能对怪物造成的最大伤害。

示例1

输入

复制1 4 2 8 2 3

1
4 2 8
2 3

输出

复制8

8

说明

显然可以在机器人坐标位于 (2,3)\mathrm{(2,3)}(2,3) 时,将坐标 2\mathrm{2}2 与坐标 3\mathrm{3}3 建上墙壁,等待机器人墙壁之间走两个来回后破坏墙壁。机器人遇到怪物自爆时消耗的总时间为 3+1+1+1+2=8\mathrm{3+1+1+1+2=8}3+1+1+1+2=8。

示例2

输入

复制1 15 4 28 4 6 10 13

1
15 4 28
4 6 10 13

输出

复制27

27

说明

一种可行的方案是,当机器人行进过程中,坐标位于 (4,10)\mathrm{(4,10)}(4,10) 之间时,将坐标 4\mathrm{4}4 与坐标 10\mathrm{10}10 建上墙壁,等待机器人在墙壁间走一个来回后破坏墙壁,让机器人走向怪物爆炸。此时机器人消耗的总时间为 10+6+11=27\mathrm{10+6+11=27}10+6+11=27。

示例3

输入

复制2 4 1 3 2 23 4 43 13 17 9 2

2
4 1 3
2
23 4 43
13 17 9 2

输出

复制0 39

0
39

做法

#include<bits/stdc++.h>
using namespace std;
int T,n,m,t;
int x[200010];
int a[200010];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=m;i++) scanf("%d",&x[i]);
        sort(x+1,x+1+m);
        for(int i=1;i+1<=m;i++){
            a[i]=x[i+1]-x[i];
        }
        if(n==1){
            cout<<1<<endl;
            continue;
        }
        if(t<n){
            cout<<0<<endl;
            continue;
        }
        if(n==t){
            cout<<t<<endl;
            continue;
        }
        vector<int> dp(t-n+1);
        dp[0]=1;
        for(int i=1;i<=m-1;i++){//考虑了前i个
            for(int j=t-n;j>=0;j--){//当前走了时间j
                for(int k=0;k<=(t-n-j/(a[i]*2));k++){//当前的选k个
                    if(j>=k*a[i]*2){
                        dp[j]=max(dp[j],dp[j-k*a[i]*2]);
                    }
                }
            }
        }
        for(int j=t-n;j>=0;j--){
            if(dp[j]==1) {
                cout<<n+j<<endl;
                break;
            }
        }
    }
}

超时了。

正解

#include<bits/stdc++.h>//完全背包
using namespace std;
int T,n,m,t;
int x[200010];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=m;i++) scanf("%d",&x[i]);
        sort(x+1,x+1+m);
        set<int> s;
        for(int i=1;i+1<=m;i++){
            s.insert(x[i+1]-x[i]);
        }
        if(n==1){
            cout<<1<<endl;
            continue;
        }
        if(t<n){
            cout<<0<<endl;
            continue;
        }
        if(n==t){
            cout<<t<<endl;
            continue;
        }
        vector<int> dp(t-n+1);
        dp[0]=1;
        for(auto u:s){
            for(int j=0;j<=t-n;j++){//必须这样顺序遍历才能正确更新
                if(j>=u*2)
                    dp[j]=max(dp[j],dp[j-u*2]);
//                 for(int k=0;k<=(t-n-j/(u*2));k++){多余了,不用管他选了多少个
//                     if(j>=k*u*2){
//                         dp[j]=max(dp[j],dp[j-k*u*2]);
//                     }
//                 }
            }
        }
        for(int j=t-n;j>=0;j--){
            if(dp[j]==1) {
                cout<<n+j<<endl;
                break;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值