AtCoder Beginner Contest 219 H
假设我们按照蜡烛的坐标从小到大排序,那么我们每到达一个蜡烛,肯定要熄灭它。
所以,在任意时刻我们走过的蜡烛都是一个区间。
按坐标正负分成部分,分别按到的距离排序。我们设表示熄灭了负数坐标蜡烛的前个,正数坐标的前个,当前在最左侧还是最右侧。
转移就可以枚举下一次走到负数坐标的项还是整数坐标的项。
但有一个问题:是维护时间,还是维护目前熄灭蜡烛的最大长度?
发现两者一个最优,另一个未必最优。我们将任何一个放入中代表一个维度,此时就可以了,但状态数太大了。
有种解决方案。
我们设表示方案所需的时间,表示目前熄灭蜡烛的长度和。
我们一个中的元素可以存若干个状态,但是若存在状态,使得并且,那么就可以不要了。(因为的时间比小,而熄灭蜡烛的长度比大)
然而这样剩余的状态还是太多。考虑使用剪枝,具体的,若存在状态,若,则可以不要。
时间复杂度玄学,不过是很快的,比还要优秀。
第二种方案:我们发现可以维护,和之前的一样,而的意思是,我们在剩下的蜡烛中还有个是在燃烧完被熄灭的。
那么转移的时候,假设到下一个所花费的时间是,那么在时间内,这个蜡烛的长度都会减,故这个蜡烛的长度共减去了。到下一个蜡烛时,我们有种选择:它在这个蜡烛中,则将转移到,否则将转移到。
那么有一个问题,万一这个蜡烛中有的燃烧成了负数呢?
可以发现,若中有蜡烛燃烧成负数,那就会对答案造成一个负的贡献,不妨抛掉这些蜡烛,也就是取会更优,所以不必考虑。
时间复杂度为。
#include<bits/stdc++.h>
using ll=long long;
const int maxn=305;
int n,m1,m2;
ll f[maxn][maxn][maxn][2];
bool vis[maxn][maxn][maxn][2];
std::pair<ll,ll> a[maxn],b[maxn];
ll dfs(int l,int r,int k,int where) {
if(k==0) return 0;
if(vis[l][r][k][where]) return f[l][r][k][where];
ll &ret=f[l][r][k][where]; ret=-1e18;
vis[l][r][k][where]=1;
ll now,nxt;
if(where==0) now=a[l].first;
else now=b[r].first;
for(int i=0;i<2;i++) {
int l_=l,r_=r,len;
if(i==0) nxt=a[l+1].first,len=a[l+1].second,l_++;
else nxt=b[r+1].first,len=b[r+1].second,r_++;
ll dis=abs(now-nxt);
if(l_>m1||r_>m2) continue;
ret=std::max(ret,dfs(l_,r_,k,i)-k*dis);
ret=std::max(ret,dfs(l_,r_,k-1,i)+len-k*dis);
}
return ret;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
ll ix,ia;
scanf("%lld%lld",&ix,&ia);
if(ix<0) a[++m1]=std::make_pair(ix,ia);
else b[++m2]=std::make_pair(ix,ia);
}
a[0]=std::make_pair(0ll,0ll),b[0]=std::make_pair(0ll,0ll);
std::sort(a+1,a+m1+1,std::greater<std::pair<ll,ll>>());
std::sort(b+1,b+m2+1);
ll ans=0;
for(int i=1;i<=n;i++)
ans=std::max(ans,dfs(0,0,i,0));
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话