当前位置: 首页 > news >正文

CSP2025模拟赛2(2025.7.26)

—— From nfls 2025 Summer Camp

目录

  • T1(easy,100%)
  • T2 (easy,100%)
  • T3(mid,75%)
  • T4 (hard-,30%)
  • 总结

T1(easy,100%)

link
简单二分。

T2 (easy,100%)

link
简单二分+小学奥数。

T3(mid,75%)

弱化版:link
题意
有长为 lenlenlen 的桥,桥上有 nnn 个石头,石头的位置为 posipos_iposi ,一只青蛙从 000 位置开始跳,每次可以跳 [s,t][s,t][s,t] 区间中任意正整数步,青蛙不一定必须站在石头上。求青蛙跳到 ≥len\ge lenlen 时最少需要踩到的石子数。   1≤n≤100,1≤s≤t≤300,1≤len≤1091 \le n \le 100,1 \le s \le t \le 300,1 \le len \le 10^91n100,1st300,1len109

思路1
注意石子位置无序,所以先排序!!!
首先有一个暴力 DP ,记 fif_ifi 为跳到 iii 位置最少需要踩到的石子数。 fi=min{fj}+stone[i]f_i=min\{f_j\} + stone[i]fi=min{fj}+stone[i] ,其中 s≤i−j≤ts \le i-j \le tsijt ,复杂度 O(len⋅t)O(len \cdot t)O(lent) 。看到 lenlenlen 十分不友好,有一个优化 DP 状态方法叫 路径压缩 (也可以是状态压缩)。
看到 nnn 很小,如果相邻两个石头位置差距很大,那么中间的没有石头的位置的转移作用不大,直接平移就好,考虑压缩。
如果 stone[i] - stone[i-1] >= t * (t - 1) ,那么 stone[i] = stone[i-1] + t * (t - 1) ,这个很好证明。
转移和原来区别不大,复杂度勉强可行。

思路2(更优解)
同样是优化原始暴力 DP ,同样考虑压缩转移中没用的部分。参考思路如下:

显然只有每个石头的前后 ttt 个点,终点后 ttt 个点以及起点有用。其他点的答案一定是单调(也就是和旁边的点的 dpdpdp 值相同)的。取出终点的点是为了方便找到答案。那么总共最多有 2nt+t+12nt+t+12nt+t+1 个点。
把这些点取出来,离散,然后转移。有两种转移,一种是普通的转移(即每次走 [s,t][s,t][s,t] 步),一种是跳过一大段区间(需要保证中间没有石头)。
关于跳过一大段区间判断是否可达,只需要判断是否有一次跳,使得跳跃区间覆盖了区间右端点。(第 k+1k+1k+1 次跳时,跳跃区间即 x+ks,x+ktx+ks,x+ktx+ks,x+kt ,找到 x+ktx+ktx+kt 第一次大于等于右端点的地方,判断左端点位置即可。)
这个跳过一大段区间的转移要一直做到碰到下一个石头位置。这样最多也只有 2t2t2t 个点需要转移,并不是很多。复杂度 O(nt2)O(nt^2)O(nt2)

//参考代码:(思路2)
//code by nfls CalvinJin
#include<bits/stdc++.h>
using namespace std;
const int N=110,M=310,INF=0x3f3f3f3f;
int A[N],dp[N*M*2],B[N*M*2];
bool Stone[N*M*2];
int main(){int L,s,t,n,i,j,k,h=0; scanf("%d%d%d%d",&L,&s,&t,&n);for (i=1;i<=n;i++){scanf("%d",&A[i]);for (j=max(0,A[i]-t);j<=A[i]+t;j++)B[++h]=j;}for (j=max(0,L-t);j<=L+t;j++)B[++h]=j;B[++h]=0;sort(B+1,B+1+h);h=unique(B+1,B+1+h)-B-1;for (i=1;i<=n;i++)Stone[lower_bound(B+1,B+1+h,A[i])-B]=1;memset(dp+2,63,sizeof(int)*1*(h-1));int Ans=INF;for (i=1;i<=h;i++){if (dp[i]==INF) continue;j=i+1; bool sto=0;dp[i]+=Stone[i];while (j<=h && B[j]-B[i]<=t){if (B[j]-B[i]>=s) dp[j]=min(dp[j],dp[i]);sto|=Stone[j];j++;}for (k=j;k<=h;k++){sto|=Stone[k];if (sto) break;int dis=B[k]-B[i];if (s*((dis+t-1)/t)<=dis) dp[k]=min(dp[k],dp[i]);}if (B[i]>=L) Ans=min(Ans,dp[i]);}printf("%d ",Ans);return 0;
}

反思
我的考场思路类似 思路2 ,也知道那些转移的点是有用的,但是最后压缩 DP 状态的时候发现会有漏的情况,也就是跳过一大段空白区间的时候考虑不当,其实直接作除就好,像上面代码。当然 思路2 虽然慢一点但是更好写。其实场上看错题也浪费了不少时间,当然还忘记将石子位置排序了!!!

代码思路1
没发现复杂度是对的,于是还加上了线段树优化,但极限数据会卡空间。

#include<bits/stdc++.h>
using namespace std;
const int maxl=4e6+5,maxn=105;struct TREE{ int mi; }tree[maxl*4];
void Build(int rt,int l,int r){tree[rt].mi=1e9;if(l==r) return;int mid=(l+r)>>1;Build(rt*2,l,mid),Build(rt*2+1,mid+1,r);
}void Modify(int rt,int l,int r,int x,int y){if(l==r){tree[rt].mi=y;return;}int mid=(l+r)>>1;if(x<=mid) Modify(rt*2,l,mid,x,y);else Modify(rt*2+1,mid+1,r,x,y);tree[rt].mi=min(tree[rt*2].mi,tree[rt*2+1].mi);
}int Query(int rt,int l,int r,int x,int y){if(x<=l&&r<=y) return tree[rt].mi;int mid=(l+r)>>1,s=1e9;if(x<=mid) s=min(s,Query(rt*2,l,mid,x,y));if(y>mid) s=min(s,Query(rt*2+1,mid+1,r,x,y));return s;
}int a[maxn],b[maxn],f[maxl],hv[maxl];
int main(){freopen("river.in","r",stdin);freopen("river.out","w",stdout); int len,s,t,n; scanf("%d%d%d%d",&len,&s,&t,&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1);if(s==t){int ans=0;for(int i=1;i<=n;i++)ans+=(a[i]%s==0);cout<<ans;return 0;}a[0]=b[0]=0;for(int i=1;i<=n;i++){b[i]=b[i-1]+min(a[i]-a[i-1],20000);hv[b[i]]=1;}Build(1,0,b[n]+t);f[0]=0,Modify(1,0,b[n]+t,0,0);for(int i=s;i<=b[n]+t;i++){f[i]=Query(1,0,b[n]+t,max(i-t,0),max(i-s,0))+hv[i];Modify(1,0,b[n]+t,i,f[i]);}int ans=1e9;for(int i=b[n];i<=b[n]+t;i++)ans=min(ans,f[i]);printf("%d",ans);return 0;
}

T4 (hard-,30%)

link
题意
给出一条线段,在左端点 000 点与右端点 n+1n+1n+1 间有 nnn 个点,并且在 000xxx 之间的所有点都是有油的,在每个点钻井判断是否有油需要时间 aia_iai ,求能够知道的最坏情况下最少需要多少时间。( xxx 未知)   1≤n≤6000,1≤ai≤1061 \le n \le 6000, 1 \le a_i \le 10^61n6000,1ai106

思路
想到 区间DP ,记 fl,rf_{l,r}fl,r 表示在能确定 xxx 不在区间 [l,r][l,r][l,r] 内,或确定 xxx 在区间 [l,r][l,r][l,r] 内某个位置的最坏情况下的最小值。 k∈[l,r]k \in [l,r]k[l,r]fl,r=min{ak+max{fl,k−1,fk+1,r}}f_{l,r} = min \{a_k + max \{f_{l,k-1},f_{k+1,r}\}\}fl,r=min{ak+max{fl,k1,fk+1,r}} 。显然超时。
考虑拆开内层的 maxmaxmax 。发现 当 l,rl,rl,r 固定时,随着 kkk 递增,fl,k−1f_{l,k-1}fl,k1 单调不降, fk+1,rf_{k+1,r}fk+1,r 单调不增。所以一定可以找到一个分界点 xxx ,当 k∈[l,x]k\in [l,x]k[l,x] 时,fl,k−1>fk+1,rf_{l,k-1} \gt f_{k+1,r}fl,k1>fk+1,r ;当 k∈[x+1,r]k \in [x+1,r]k[x+1,r] 时,fk+1,r>fl,k−1f_{k+1,r} \gt f_{l,k-1}fk+1,r>fl,k1 。所以上述转移拆分为:
fi,j={min{ak+fl,k−1}k≤xmin{ak+fk+1,r}k>xf_{i,j}=\begin{cases} min\{a_k+f_{l,k-1}\} & k \le x \\ min\{a_k+f_{k+1,r}\} & k \gt x \end{cases}fi,j={min{ak+fl,k1}min{ak+fk+1,r}kxk>x
观察得到,若 lll 固定,随着 rrr 递增,xxx 单调不降;同理 若 jjj 固定, lll 递减, xxx 单调不增。
所以对于每一个 lll ,可以开一个单调队列维护第 lll 行每一个 xxx ;同理对于每一个 rrr ,也可以开一个单调队列维护每一列上的每一个 xxx
当然实际操作中,由于 lll 是第一层循环, rrr 为第二层循环,所以只用开一个单调队列维护每一行,重复利用即可;而要开 nnn 个维护每一列。至于 lll 倒序枚举是因为若 rrr 固定时,lll 是从 nnn111 转移的。

反思
场上其实没看太懂题,想不到区间 DP ,只做了 aia_iai 都相等 这一档分,但其实 DP 部分也很好想,单调性也很好发现,重要是将单调性归入单调队列中维护比较厉害。所以 DP 优化时可以考虑单调性。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=6005;#define A(x) a[x]+f[i][x-1] // 左边的代价
#define B(x) a[x]+f[x+1][j] // 右边的代价int a[maxn],f[maxn][maxn],ql[maxn],qr[maxn][maxn],hr[maxn],tr[maxn];
int main(){freopen("oil.in","r",stdin);freopen("oil.out","w",stdout); int n; cin>>n;for(int i=1;i<=n;i++){cin>>a[i];f[i][i]=a[i];//!hr[i]=1;}for(int i=n;i>=1;i--){int hl=1,tl=0;ql[++tl]=i;// 单调队列维护左边代价不小于右边的转移qr[i][++tr[i]]=i;// 单调队列维护右边代价不小于左边的转移for(int j=i+1;j<=n;j++){while(hl<=tl&&A(ql[hl])<B(ql[hl])) hl++;while(hl<=tl&&A(j)<A(ql[tl])) tl--;ql[++tl]=j;while(hr[j]<=tr[j]&&B(qr[j][hr[j]])<A(qr[j][hr[j]])) hr[j]++;while(hr[j]<=tr[j]&&B(i)<B(qr[j][tr[j]])) tr[j]--;qr[j][++tr[j]]=i;f[i][j]=min(A(ql[hl]),B(qr[j][hr[j]]));}}cout<<f[1][n];return 0;
}

总结

怎么越来越不会做题了呢。

http://www.dtcms.com/a/300137.html

相关文章:

  • 机器人仿真(2)Ubuntu24.04下RTX5090配置IsaacSim与IsaacLab
  • Jenkins持续集成工具
  • swagger基本注解@Tag、@Operation、@Parameters、@Parameter、@ApiResponse、@Schema
  • (1-7-4) MySql 的高级查询
  • 20250726-2-Kubernetes 网络-Service 定义与创建_笔记
  • 【Spring Cloud】微服务学习
  • 超时进行报警例子
  • 在 Windows 系统中实现 WinToGo 的 VHDX 文件切换使用的常见方法
  • 什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
  • Spring AI Alibaba Video 示例
  • 大型微服务项目:听书——12 数据一致性自定义starter封装缓存操作
  • Java设计模式之行为型模式(中介者模式)实现方式与测试方法
  • 大数据之路:阿里巴巴大数据实践——实时技术与数据服务
  • Reeden:跨平台 AI 电子书阅读器
  • leetcode112, 257:二叉树的路径总和、二叉树的所有路径双题对比
  • 【基础完全搜索】USACO Bronze 2020 December - 雏菊链Daisy Chains
  • AI Agent:自主决策的智能助手
  • Javaweb————HTTP消息体拆分讲解
  • ISIS分片扩展实验案例
  • 精密全波整流电路(四)
  • 2025年02月11日 Go生态洞察:Go 1.24 发布亮点全面剖析
  • 【DNS服务配置—实现正反向解析】
  • 宏观杠杆率及其数据获取(使用AKShare)
  • 【C++基础】指针常量 | 常量指针 | int* p | const int* p | int* const p| const int* const p
  • MyBatis-Plus 进阶功能:分页插件与乐观锁的实战指南
  • Codeforces Round 181 (Rated for Div. 2)
  • 哈尔滨←→南昌的铁路要道
  • 计算公式放配置
  • 【linux】keepalived
  • ART某板卡的软件位置