压缩dp之noip提高组2005 过河

来源:互联网 发布:淘宝怎么选购液晶电视 编辑:程序博客网 时间:2024/06/07 06:51

题目链接:https://vijos.org/p/1002


这题拿到手,很容易想到这样的一个dp方程:

     f(i) = minof{f(i-j)+a[i]|j∈[s,t],且i-j>=0}

其中f(i)表示调到i位置最少需要的石子,a[i]表示i位置是否有石子,1表示有,0表示没有。


但是这题的L范围达到10^9,如果直接采用上面的dp方程只能拿到30分。

如何改进呢?我们很快注意到石子个数m最大不差过100,那么显然m个石子分布在很大的L上,  必然会存在相邻的石子距离非常大,那么f(i)会出现在较长的区间内值是一定的!

也就是说,这段很长的距离的f(i)值没必要继续一个个就算,因为这部分的f(i)和前面部分的f(i)是一样的,换句话说,其对答案没有贡献,只是起到一个“桥梁”、“传递”的作用,但是对我们来说,我们完全可以忽略这部分,直接跳到下一个石子!显然,如此一来,时间必然过得去。

那么忽略的部分是多少呢?换句话说,相邻石子距离压缩多少而不影响结果呢?当然我们希望越小越好。


那么我们只要知道多长距离以后,f(i)的值达到稳定。

上图中, x表示石子位置。其实从x+2T+1之后,f(i)开始稳定,所以我们最多只需要保留2*T的距离!

但是如果这样提交的话,我的程序只能拿到80分,有两个case过不去,发现都是s==t的case。

仔细想想,会发现,上面要想出现这种“传递"的作用,其实必须是局部可连续的,那么单s==t,就会导致所能跳达的点都是离散的,不符合前述的压缩做法。

所以加特判即可ac。详细代码如下:

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cctype>#include <cmath>#include <vector>#include <stack>#include <queue>#include <string>#include <list>#include <deque>#include <map>#include <set>#include <algorithm>using namespace std;#define INF 0x3f3f3f3f#define EXP 1e-8#define LL long longint L,S,T,m;int main(){freopen("river.in","r",stdin);freopen("river.out","w",stdout);scanf("%d",&L);scanf("%d%d%d",&S,&T,&m);vector<int> a(m+20); int x;for (int i = 1; i <= m; ++i){scanf("%d",&a[i]);}sort(a.begin()+1,a.begin()+m+1);//printf("%d %d\n",a[0],a[1]);if (S == T){int ans = 0;for (int i = 1; i <= m; ++i){if (a[i] % S == 0){++ans;}}printf("%d\n",ans);return 0;}vector<int> stone(25*m);int j = 0;for (int i = 1; i <= m; ++i){if (a[i]-a[i-1] > 2*T){j += 2*T;stone[j] = 1;}else{j += a[i]-a[i-1];stone[j] = 1;}}vector<int> f(25*m);f[0] = 0;int k = j + T;for (int i = 1; i <= k; ++i){int maxs = INF;for (int j = S; j <= T; ++j){if (i-j>=0){maxs = min(maxs,stone[i]+f[i-j]);}}f[i] = maxs;}printf("%d\n",f[k]);/*for (int i = 0; i <= L+T; ++i){printf("%d %d\n",i,f[i]);}*///system("pause");return 0;}


0 0
原创粉丝点击