天天看點

【基礎練習】【線性DP+離散化】codevs1105 過河題解

題目描述 Description

在河上有一座獨木橋,一隻青蛙想沿着獨木橋從河的一側跳到另一側。在橋上有一些石子,青蛙很讨厭踩在這些石子上。由于橋的長度和青蛙一次跳過的距離都是正整數,我們可以把獨木橋上青蛙可能到達的點看成數軸上的一串整點:0,1,……,L(其中L是橋的長度)。坐标為0的點表示橋的起點,坐标為L的點表示橋的終點。青蛙從橋的起點開始,不停的向終點方向跳躍。一次跳躍的距離是S到T之間的任意正整數(包括S,T)。當青蛙跳到或跳過坐标為L的點時,就算青蛙已經跳出了獨木橋。

題目給出獨木橋的長度L,青蛙跳躍的距離範圍S,T,橋上石子的位置。你的任務是确定青蛙要想過河,最少需要踩到的石子數。

輸入描述 Input Description

輸入第一行有一個正整數L(1<=L<=109),表示獨木橋的長度。第二行有三個正整數S,T,M,分别表示青蛙一次跳躍的最小距離,最大距離,及橋上石子的個數,其中1<=S<=T<=10,1<=M<=100。第三行有M個不同的正整數分别表示這M個石子在數軸上的位置(資料保證橋的起點和終點處沒有石子)。所有相鄰的整數之間用一個空格隔開。

輸出描述 Output Description

輸出隻包括一個整數,表示青蛙過河最少需要踩到的石子數。

樣例輸入 Sample Input

10

2 3 5

2 3 5 6 7

樣例輸出 Sample Output

2

資料範圍及提示 Data Size & Hint

資料規模

對于30%的資料,L<=10000;

對于全部的資料,L<=109。

方程顯然:

f[i] = min { f[i - x] } + stone[i];  x∈[S, T]
ans = min {f[j]};  j∈[L, L+T-1]
           

既然資料這麼大,顯然要離散化

一開始離散的t,但是怎麼也過不了 不知是不是楊志燦老師的課件有誤。codevs上也有說t的,不知為什麼;

引用codevs上最小公倍數做法的解釋:

隻要求出1--10裡面任意兩個數的最小公倍數,然後取最大的,可以證明當兩石塊之間的距離大于它的時候,那麼大于它的部分的每一個點都可以通過這兩個數的某一種組合跳到,是以當兩個石塊間的距離大于這個最小公倍數,那麼就把它們的距離縮小到這個最小公倍數.

路徑壓縮後,就可以DP求出最小踩石子個數。設f[i]表示到達第i個位置最小踩多少個石子.

則f[i]=min(f[i-j]+d[i])(1<=i<=l+t)(s<=j<=t),d[i]表示第i個位置是否有石子.

最後的答案就是在l to l+t 之間找最小。

雖然不知道怎麼回事,但就是這個道理= =

上代碼,我要抓緊寫作業去

//codevs1105 青蛙過河 線性DP+離散化
//copyright by amtake
#include
   
    
#include
    
     
#include
     
      
using namespace std;

int l,s,t,m;
const int maxn=10000+10;
const int tt=90;
int a[100+10],f[maxn];
int have[maxn];

int main()
{
    freopen("1.txt","r",stdin);
    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);
    a[m+1]=l;
    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;
    }
    for (int i=0;i<=m;i++)
    {
        //int x=a[i+1]-a[i];
        //while (x>t) x-=t;
        //a[i+1]=a[i]+x; 
        if (a[i+1]-a[i]>tt) a[i+1]=a[i]+(a[i+1]-a[i])%tt;
        if (a[i+1]==a[i]) a[i+1]+=tt;
    }
    memset(have,0,sizeof(have));
    for (int i=1;i<=m;i++)
    {
        have[a[i]]=1;
    }
    memset(f,0x3f,sizeof(f));
    for(int i=s;i<=t;i++) f[i]=have[i];
    for (int i=s<<1;i<=a[m+1]+t;i++)
    {
        for (int j=s;j<=t;j++)
        {
            if(j<=i)f[i]=min(f[i-j],f[i]);else break;
        }
        f[i]+=have[i];
    }
    int ans=0x3f3f3f3f;
    for (int i=a[m+1];i<=a[m+1]+t;i++) if (f[i]
      
     
    
   
           

——春來遍是桃花水,不辨仙源何處尋。

繼續閱讀