首页 > 代码库 > [JZOJ P1309] [DP]过河
[JZOJ P1309] [DP]过河
@kaike
早上惯例迟到...怪我咯...每天都睡到不省人事八点才醒不迟到才怪,这么说明老师要把迟到当做常态毕竟这是世界十大不可控制事件之一。
意外遇到子桢大神..还以为是老师23333不过没事晃悠在我身边看看我在干嘛然后低头打游戏是要咋喵,嘘,背着子桢写博文是很难的。
废话不多说了传送门走起。
DP:
这题在noip的时候就见过..这么说来很久辣,第一次遇到状态压缩= =还有DP,就在我开心的写出方程式的时候发现打不出来..
f[i]=min{f[i],f[j]+rock[i]};(S<=j<=T) f[i]是未知点的最小石子,f[j]是已知点的最小石子,rock[i]是看在i位置上是否有石子,0 表示无石子 1 表示有石子 直接相加即可。 提前把 f[i]全赋值为正无穷,f[0]=0;
第一个循环是以起点S终点L+T-1来操作的,用已知点求未知点一步步往后退,就是用两个循环,一个前一个后,来推出前面那个未知点最符合的方案,这也满足最优性原理QAQ。
第二个循环以i-T为起点i-S为终点,表示未知点的前一步的已知点的未知。然而 i-T<i-S.
最后答案就是在 f[L]-f[L+T-1]中找到可以到达的点并且中间石子数最少。 //当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。 找可以到达的点就跟之前的赋值正无穷有关,若是正无穷则不可能,就是如此easy。
状态压缩:
除了S==T的特殊情况,题中隐含了一个1 <= S <= T <= 10 的特殊情况,也就是说 maxS=9,maxT=10,根据什么乱七八糟的公倍数可以算出他们之间状态可以为90的倍数,如果两个石子中间相差90多的话,就可以直接膜去,可以认为90就是重复的片段,任何大于90的都可以认为用小于90的情况来处理。 a[i]=a[i-1]+(a[i]-a[i-1])%90;
其中一个小优化就是可以认为 a[M+1]=L 把终点也当做一个石子来缩短距离。
这样就差不多惹,其中当S==T时特判就ok,第一次遇到状态压缩具体怎么推出来还不清楚,以后遇到状态压缩一定拿来跟这道题比较。
今天就写了一道题,效率低的不止几倍,我不想多说话辣。
#include<iostream> #include<algorithm> #include<cstring> using namespace std; int L,S,T,M,ans=0; int a[101],f[100010]; bool bok[100010]; int main() { cin>>L>>S>>T>>M; for(int i=1;i<=M;i++) cin>>a[i]; if(S==T){ for(int i=1;i<=M;i++) if(a[i]%S==0) ans++; cout<<ans; return 0; } sort(a,a+M+1); a[M+1]=L; for(int i=0;i<=M;i++) if(a[i+1]-a[i]>90) a[i+1]=a[i]+(a[i+1]-a[i])%90; for(int i=1;i<=M;i++) bok[a[i]]=1; memset(f,-1,sizeof(f)); f[0]=0; L=a[M+1]; for(int i=S;i<=L+T-1;i++) for(int j=i-T;j<=i-S;j++) if(j>=0&&f[j]!=-1) { if(f[i]==-1) f[i]=f[j]+bok[i]; else f[i]=min(f[i],f[j]+bok[i]); } ans=10000; for(int i=L;i<=L+T-1;i++) if(f[i]!=-1) ans=min(ans,f[i]); cout<<ans; return 0; }
[JZOJ P1309] [DP]过河