vijos 1002 过河(一类压缩长度的DP)

地址:https://vijos.org/p/1002

题意:一条直线上有m个点,青蛙一次能跳的长度为s~t,每个点的坐标范围1~10^9,1<=s<=t<=10

分析:这题一下子就能想到简单的DP,f[ i ] =min{ f[ j +k ] }+w  (s<=k<=t, w为i坐标是否有石子),这样的复杂度是O(L*t)的,主要时间都在L上,由于石头数m比较少,我们应该会发现,实际上有许多石头相隔很远,这之间的状态一直都是相同的,所以没必要全部考虑,而是直接跳过,我们可以将这座桥分段,每段为石子加上它两端的t个坐标,如果两个石子太近,就把他们和起来成为一段,这样最多只有m段,现在需要更新的状态只有相邻两段之间的状态转移,还有本段自身的转移,本段自身的转移方程很简单,就是上面的方程,而两段之间的转移复杂一点,就是前一段的后t个坐标和,当前段的前t个坐标,注意这里只能取t个才能保证之间没有石子,这也是状态不改变的前提,现在就是枚举前面t个到当前t个是否可达了,可达就更新,在这里我郁闷了好久,数论功底太弱= =,现在总结一下:

坐标i到j是否可达,也就是x*s+y*t = j-i+1,这个式子不就是典型的二元一次不定方程ax+by=c

一般的不定方程我们只求出可行解就行了,而这里我们需要的是非负解,因为题目要求青蛙一直向终点跳。。。

所先,省略一堆证明,我们可以用拓展欧几里德求出一组特解x0,y0,或则判断无解

现在重点就是根据这组特解求出非负解,我们知道x=x0-bt, y=y0+at, t=0.+-1, +-2,+-3...也是方程的解,现在要求x=x0-bt>=0, y=y0+ay>=0

得到(-y0/a)<=t<=(x0/b)现在我们只要判断这个区间是否有一个整数就行了,有整数说明有解


现在我们可以判断任意长度是否可以跳到了,所以整个解法完成,剩下的就是代码了

有个细节就是石子的坐标无序

PS:这题好久以前怎么写的忘了,记得好简单的啊。。。不过当时好像是暴力拓展105左右的长度来保证正确性,证明我也不会,总之数学弱渣了。。。


代码越写越挫:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int mm=111111;
const int mn=111;
struct data
{
    int s,t,p;
}g[111];
int f[mm],a[mn],b[mm];
int i,j,s,t,m,n,l,ans;
int getp(int u, int x)
{
    return g[u].p+x-g[u].s;
}
int extended_euclid(int a, int b, int &x, int &y)
{
    if(b == 0) // gcd(a, b) == a
    {
        x = 1;
        y = 0;
        return a;
    }
    int n = extended_euclid(b, a%b, x, y);
    int tmp = x;
    x = y;
    y = tmp - (a/b)*y;
    return n;
}
int ok(int d)
{
    int x,y,n;
    n=extended_euclid(s,t,x,y);
    if(d%n)return 0;
    x=x*(d/n);
    y=y*(d/n);
    for(n=-y/s-1;n<=x/t+1;++n)
        if(-y<=s*n&&n*t<=x)return 1;
    return 0;
}
void dp(int u, int v)
{
    int i,j,k;
    for(i=g[v].s;i<=g[v].t;++i)
        for(b[getp(v,i)]=0,j=1;j<=m-2;++j)
            if(a[j]==i)++b[getp(v,i)];
    if(v>1||g[v].s>0)
        for(i=max(g[u].t-t,g[u].s);i<=g[u].t;++i)
            for(j=g[v].s;j<=min(g[v].s+t,g[v].t);++j)
                if(ok(j-i))f[getp(v,j)]=min(f[getp(v,j)],f[getp(u,i)]+b[getp(v,j)]);
    for(i=g[v].s+s;i<=g[v].t;++i)
    {
        for(j=s;j<=t;++j)
            if(i-j>=g[v].s)f[getp(v,i)]=min(f[getp(v,i)],f[getp(v,i-j)]+b[getp(v,i)]);
    }
}
int main()
{
    scanf("%d",&l);
    scanf("%d%d%d",&s,&t,&m);
    for(i=1;i<=m;++i)
        scanf("%d",&a[i]);
    sort(a+1,a+m+1);
    a[++m]=l;
    a[++m]=2e9;
    n=0;
    g[0].s=g[0].t=g[0].p=0;
    for(j=1,i=2;i<=m;++i)
        if(a[i]>a[i-1]+2*t)
        {
            ++n;
            g[n].s=max(0,a[j]-t);
            g[n].t=a[i-1]+t;
            g[n].p=g[n-1].p+g[n-1].t-g[n-1].s+1;
            j=i;
        }
    memset(f,10,sizeof(f));
    f[0]=0;
    if(g[1].s==0)f[1]=0;
    for(i=1;i<=n;++i)dp(i-1,i);
    ans=m+1;
    for(i=g[n].s;i<=g[n].t;++i)
        if(i>=l&&f[g[n].p+i-g[n].s]<ans)
            ans=f[g[n].p+i-g[n].s];
    printf("%d\n",ans);
    return 0;
}


发布了409 篇原创文章 · 获赞 16 · 访问量 61万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览