luogu P1052 过河
Posted qxyzili--24
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P1052 过河相关的知识,希望对你有一定的参考价值。
先看一下题面。
背景
这道2005年的提高组时至今日仍然评级在提高+/省选-,由此可见这题确实不简单。
虽然都说是很简单的DP了,但是坑点依然很多。
分析
不过我开始并没有看出来是DP。
那自然考虑搜索啦~
首先考虑搜索在渡河的途中,踩到的最小石子数量。
然而1..10^9长度的桥。就算是O(n)的算法也不能在一秒内出解。
那么尝试去搜搜石子吧?
搜一个石子就需要考虑来源和去路,导致算法更困难了。。。
那么就只能DP了啊。
以石子分阶段的一维动规,时间复杂度是O(n^2)。
最多也只有100×100的时间。但是这样分状态就十分复杂。
因为石头的分布是没有任何规律,而且会有后效性。
???????????????
这题怎么做啊?????????
然后我就去买了杯可乐,回来写了个爆搜加了点剪枝,搞了一组数据。
然后蒟蒻我就开始面向数据编程研究数据,然后我就发现了一个神奇的东西¿
虽然桥的长度......
然而,石子的数量......【日常看数据范围不看全系列
吼啊,离散化准备¿
????????????????
真?dp+离散化?
可以的!
方法一:DP+离散化
首先轻松写出DP方程
f [ i ] = min( f [ i−j ] + flag [ i ] )(s<=j<=t)
我们可以发现当两点间的距离d大于t时,一定可以由d%t跳过来。
所以最多只需要t+d%t种距离的状态,就可以表示这两个石子之间的任意距离关系。
这样就把题目中的10^9压缩成了2*t*m最多不超过2000,然后就可以放心大胆地用DP了
代码奉上【同机房大佬友情赞助 这么丑代码怎么可能是我写的
#include<bits/stdc++.h> using namespace std ; long long l,s,t,m,a[20010],dis[20010],ans,flag[20010]; long long dp[20010]; int main() scanf("%lld",&l); scanf("%lld%lld%lld",&s,&t,&m); if(s == t) long long what; for(int i=1;i<=m;i++) scanf("%lld",&what); ans += ((what % s) == 0); printf("%lld\\n",ans); return 0; else long long tp = 0; for(int i=1;i<=m;i++) scanf("%lld",&a[i]); sort(a+1,a+1+m); for(int i=1;i<=m;i++) dis[i] = min(a[i] - a[i-1] , (long long)90); tp += dis[i]; flag[tp] = 1; dis[m+1] = min((long long)100,l - a[m]); tp += dis[m+1]; for(int i=1;i<=tp+9;i++) dp[i] = 0x6fffffff; for(int j=s;j<=t;j++) if(i>=j) dp[i] = min(dp[i-j] + flag[i] , dp[i]); ans = 0x6fffffff; for(int i=tp;i<=tp+9;i++) ans = min (ans, dp[i]); printf("%lld\\n",ans); return 0;
怎么可能只有一种做法?
我们来仔细想想,画个图手玩一下。
假如 s < t ,青蛙一定会跳到一个特殊的位置,s*t。
即当s < t时, s和t一定会重合(当距离为lcm(s,t)即s,t的最小公倍数时) 。
然后我们继续向后看,可以发现 s*t 以后的每个点都可以到达,。
所以我们只需将每两个石头超过 s*t 的距离缩成 s*t 就可以了
顺便带来一波数学证明。
图片来源是luogu中的 Panda_hu 的题解。
方法二:路径压缩
代码奉上
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; long long l; int s,t,M; int m[101],sum[10001]; int q[10000001]; int cmp(int a,int b) return a<b; int main() scanf("%lld%d%d%d",&l,&s,&t,&M); for(int i=1;i<=M;i++) scanf("%d",&m[i]); sort(m+1,m+M+1,cmp); int temp=0; for(int i=1;i<=M;i++) if(m[i]-m[i-1]<=t*s) temp+=m[i]-m[i-1]; else temp+=(m[i]-m[i-1])%(s*t)+s*t; q[temp]=1; memset(sum,0x3f,sizeof sum); sum[0]=0; for(int i=1;i<=temp+t;i++) for(int j=s;j<=t;j++) if(i-j<0) continue; sum[i]=min(sum[i],sum[i-j]+q[i]); int ans=0x3f3f3f3f; for(int i=temp+t-s;i<=temp+t;i++) ans=min(ans,sum[i]); cout<<ans<<endl; return 0;
AC愉快!!!!!!!!!!!
以上是关于luogu P1052 过河的主要内容,如果未能解决你的问题,请参考以下文章
noip 2005 luogu cogs P1052 过河 WD