状态转移方程非常简单,dp[i]=min(dp[i],dp[j]+b[i])
然而L高达10的9次方,但是石头的个数只有最多100个,有效的石头数目很少,但是他们分布的空间很广,符合离散化的特点,所以这道题是离散化dp
首先,这道题没有说石头的位置是按顺序输入的,所以要先对石头进行升序排序。
接下来就进行离散化操作(自己想用map搞,想了半天没想明白怎么弄,一直WA,后来看了一下别人的离散化方法,恍然大悟,接下来来解释一下)
遍历石头的位置数组,这里记作a[]
离散化处理之前石头的位置(输入的是多少就是多少)在这里称为真实位置
离散化处理之后石头的位置(进行处理,把范围进行缩小之后)在这里称为理想位置
判断现在遍历到的石头位置,与上一个石头的真实位置相减,计算出一个距离,如果这个距离是至少得用两步最大步长才可以走到的,那么说明距离太远了,将距离进行缩小。
缩小距离的方法:
1.模拟从上一个石头的真实位置走到这个石头的真实位置,采取贪心策略,每次都最大步长,得到最后一步需要走多少,用变量tmp来记录
2.用变量pre来记录这个石头的真实位置(之后离散化处理,将会改变a[i]的值,记录以便下一个石头的离散化处理)
3.将这个石头的真实位置往上一个石头的理想位置进行靠近(我们最终处理出来的所有石头理想位置,只要相对距离是正确的即可),处理的方式是如果走了很多次最大步长,那么我们只保留一步,而省略掉其他的,因为那些都是无用功,必然不可能踩到石头,直接省略掉即可。
4.当距离不算太远的时候,即两步最大步长以内可以走到,我们也不能放任他不管,因为我们对其他的石头位置都将其从真实位置改为了理想位置,如果放任不管,那么石头之间的相对距离就发生错误了,处理的方式是将这个石头的理想位置,与上一个石头的理想位置的距离,设定为这个石头的真实位置跟上一个石头的真实位置的距离,即可达到相对距离保持不变的目的。
即设这个石头的理想位置为loc
loc-a[i-1]==a[i]-pre(a[i-1]是已经处理过的,已经从真实位置被改成了理想位置)
移项可以得到loc=a[i]+a[i-1]-pre,再把loc赋给a[i]即可
这里直接写a[i]+=a[i-1]-pre就好了
以下是局部的代码实现:
for(int i=1;i<=m;i++) { if(a[i]-pre>=2*t) { tmp=(a[i]-pre)%t;//都按最长走,最后一步要走多长 pre=a[i]; a[i]=tmp+t+a[i-1]; } else { int z=a[i]; a[i]+=a[i-1]-pre; pre=z; } }
接下来是总长度L的处理(借助于最后一个石头):
同理,走多次最大步长等价为走一步最大步长,处理方式如下:
d%=t; if(d==0) d+=t; l=a[m]+d;
然后建立一个数组b[],用b[]存放所有石头的理想位置
关于dp之前的处理,就已经大功告成了。
接下来就是本题最容易想到的状态转移方程的应用。
直接将b[],dp[]两个数组运用于常规的dp方法
不过记得答案要枚举dpk,取最小值
以下是本题的完整AC代码,搞了好几个小时不容易啊:
#include <bits/stdc++.h> using namespace std; int s,t,m,l,dp[10000],a[102],b[10000]; int main(){ scanf("%d%d%d%d",&l,&s,&t,&m); for(int i=1;i<=m;i++) scanf("%d",&a[i]); sort(a+1,a+m+1); int pre=0,d=l-a[m],tmp; for(int i=1;i<=m;i++) { if(a[i]-pre>=2*t) { tmp=(a[i]-pre)%t;//都按最长走,最后一步要走多长 pre=a[i]; a[i]=tmp+t+a[i-1]; } else { int z=a[i]; a[i]+=a[i-1]-pre; pre=z; } } d%=t; if(d==0) d+=t; l=a[m]+d; for(int i=1;i<=m;i++) b[a[i]]=1; memset(dp,0x3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=l+t;i++) { for(int j=((i-t)>0?(i-t):0);j<=i-s;j++) dp[i]=min(dp[i],dp[j]+b[i]); } int ans=0x3f3f3f3f; for(int i=l;i<=l+t;i++) ans=min(ans,dp[i]); printf("%d\n",ans); }