链接:登录—专业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;
}
}
}
}