是个十足的 DP 题。刷完了 YeahPotato 的 DP 博客,你觉得有什么方法能套进来呢?
前面“基于特殊结构的技巧”没有一个能用。
如何分析性质?分析样例:
12 3 8 9 9 6 9 9 10 8 8 3 4 5
明显先排序。
3 4 5 6 8 8 8 9 9 9 9 10
猜想,一定有几个人在跑腿送灯,一定是前 C 个人的前缀,从对岸往回跑的一定是对岸用时最少的一个人。
搓出几个看上去很全的策略:(|
左侧是跑腿,最终都往回跑)
(3,4,5|)(3)(6,8,8)(4)(8,9,9)(5) (3,4|5)(3)(6,8,8)(4) (3|4,5)
然而是不对的。实际上最优的是:
- 388 过去,3 回来;
- 345 过去,3 回来;
- 899 过去,4 回来;
- 346 过去,3 回来;
- 9 9 10 过去,4 回来;
- 34 过去。
8+3+5+3+9+4+6+3+10+4+4=59。
想象最优解的结构,简化过程。发现最终的行走方案不一定等于排序的。并且还是一次带 k 个跑腿 C-k 个(或 0 个)累赘,后面留着用,每次带累赘都要耗一个跑腿。最后是一群人(<=C)过去。
这里直接篡改顺序,按排序后的数组计算,贡献提前计算。
具体地,记 fi,j 表示考虑到第 i 个人,运完第 i 个后有 j 个跑腿在对岸待命,此时最小花费。转移枚举这回几个人跑腿即可。
最后的一群如果是跑腿,直接记在初值里面。
细节,由于篡改顺序,积累的跑腿可以多于 C,但不多于 N/C。转移为 C,复杂度 O(N×N/C×C)=O(N2)。
#include<cstdio> #include<cstring> #include<algorithm> #define fi first #define se second #define mkp std::make_pair using ll=long long; using std::max; using std::min; template<class T> void cmax(T&a,T b){a=max(a,b);} template<class T> void cmin(T&a,T b){a=min(a,b);} const int NV=1e4; namespace xm{ ll s[NV+5],f[NV+5][NV+5]; int a[NV+5]; void _(){ int N,C; scanf("%d%d",&N,&C); for(int i=1;i<=N;++i) scanf("%d",a+i); std::sort(a+1,a+N+1); for(int i=1;i<=N;++i) s[i]=s[i-1]+a[i]; if(C>=N){ printf("%d\n",a[N]); return; } memset(f,0x3f,sizeof f); f[0][0]=0; for(int i=1;i<=C;++i)f[i][0]=a[i]; for(int i=0;i<=N;++i) for(int j=0;(j-1)*C<=N-i;++j) for(int k=0;k<=C&&k<=i;++k){ if(k>0) cmin(f[i][j+k-1],f[i][j]+a[k]+s[k]); if(j+k&&k<C) cmin(f[min(i+C-k,N)][j+k-1],f[i][j]+a[min(i+C-k,N)]+s[k]); } printf("%lld\n",f[N][0]); } } int main(){ xm::_(); return 0; }
你问我怎么想出来的,我认为至少得搞到这个样例的最优解,但我手头上没有可靠的暴力程序。
合集:
题解
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· 深度对比:PostgreSQL 和 SQL Server 在统计信息维护中的关键差异