列表

详情


DP29. 过河

描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

数据范围:

输入描述

第一行有一个正整数L ,表示独木桥的长度。
第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数
第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。
所有相邻的整数之间用一个空格隔开。

输出描述

只包括一个整数,表示青蛙过河最少需要踩到的石子数。

示例1

输入:

10
2 3 5
2 3 5 6 7

输出:

2

原站题解

C 解法, 执行用时: 2ms, 内存消耗: 348KB, 提交时间: 2022-06-20

#include <stdio.h>

void quickSort(int *nums, int start, int end) {
    if (start < end) {
        int rIndex = rand() % (end - start + 1) + start, tmp;
        tmp = nums[rIndex];
        nums[rIndex] = nums[start];
        nums[start] = tmp;
        
        int target = nums[start], i = start, j = end;
        while(i < j) {
            while(i < j && nums[j] > target) {
                j--;
            }
            if(i < j) {
                nums[i++] = nums[j];
            }
            while(i < j && nums[i] <= target) {
                i++;
            }
            if(i < j) {
                nums[j--] = nums[i];
            }
        }
        nums[i] = target;
        
        quickSort(nums, start, i - 1);
        quickSort(nums, i + 1, end);
    }
}

int min(int a, int b) {
    return a > b ? b : a;
}

int main() {
    int L;
    scanf("%d", &L);
    
    int s, t, m;
    scanf("%d %d %d", &s, &t, &m);
    
    int *nums = (int *)malloc(sizeof(int) * m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &nums[i]);
    }
    
    quickSort(nums, 0, m - 1);
    
    if (s == t) {
        int count = 0;
        for (int i = 0; i < m; i++) {
            if (nums[i] % s == 0) {
                count++;
            }
        }
        printf("%d", count);
    } else {
        int lastNIndex = 0;

        int *dp = (int *)malloc(sizeof(int) * 10000); //(2 * t + 1)
        int j = 0, minDP, len = 10000;
        dp[0] = 0;

        int k = s * t, d = 0, x;
        if (nums[0] > k) {
            d = nums[0] - k;
            nums[0] -= d;
        }
        for (int i = 1; i < m; i++) {
            x = nums[i] - d - nums[i - 1];
            if (x > k) {
                d += (x - k);
            }
            nums[i] -= d; 
        }

        x = L - d - nums[m - 1];
        if (x > k) {
            d += (x - k);
        }
        L -= d;

        for (int i = 1; i < s; i++) {
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
            }
            dp[i] = 101;
        }
        for (int i = s; i <= t; i++) {
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
                dp[i] = 1;
            } else {
                dp[i] = 0;
            }
        }

        for (int i = t + 1; i <= (L + t - 1); i++) {
            minDP = 101;
            for (j = t; j >= s; j--) {
                minDP = min(minDP, dp[(i-j)%len]);
            }
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
                dp[i%len] = minDP + 1;
            } else {
                dp[i%len] = minDP;
            }
        }

        minDP = 101;
        for (int i = L; i <= (L + t - 1); i++) {
            minDP = min(minDP, dp[i%len]);
        }

        printf("%d", minDP);
        
        free(dp);
    }
    
    
    free(nums);
    
    return 0;
}





C 解法, 执行用时: 3ms, 内存消耗: 336KB, 提交时间: 2022-07-12

#include <stdio.h>

void quickSort(int *nums, int start, int end) {
    if (start < end) {
        int rIndex = rand() % (end - start + 1) + start, tmp;
        tmp = nums[rIndex];
        nums[rIndex] = nums[start];
        nums[start] = tmp;
        
        int target = nums[start], i = start, j = end;
        while(i < j) {
            while(i < j && nums[j] > target) {
                j--;
            }
            if(i < j) {
                nums[i++] = nums[j];
            }
            while(i < j && nums[i] <= target) {
                i++;
            }
            if(i < j) {
                nums[j--] = nums[i];
            }
        }
        nums[i] = target;
        
        quickSort(nums, start, i - 1);
        quickSort(nums, i + 1, end);
    }
}

int min(int a, int b) {
    return a > b ? b : a;
}

int main() {
    int L;
    scanf("%d", &L);
    
    int s, t, m;
    scanf("%d %d %d", &s, &t, &m);
    
    int *nums = (int *)malloc(sizeof(int) * m);
    for (int i = 0; i < m; i++) {
        scanf("%d", &nums[i]);
    }
    
    quickSort(nums, 0, m - 1);
    
    if (s == t) {
        int count = 0;
        for (int i = 0; i < m; i++) {
            if (nums[i] % s == 0) {
                count++;
            }
        }
        printf("%d", count);
    } else {
        int lastNIndex = 0;

        int *dp = (int *)malloc(sizeof(int) * 10000); //(2 * t + 1)
        int j = 0, minDP, len = 10000;
        dp[0] = 0;

        int k = s * t, d = 0, x;
        if (nums[0] > k) {
            d = nums[0] - k;
            nums[0] -= d;
        }
        for (int i = 1; i < m; i++) {
            x = nums[i] - d - nums[i - 1];
            if (x > k) {
                d += (x - k);
            }
            nums[i] -= d; 
        }

        x = L - d - nums[m - 1];
        if (x > k) {
            d += (x - k);
        }
        L -= d;

        for (int i = 1; i < s; i++) {
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
            }
            dp[i] = 101;
        }
        for (int i = s; i <= t; i++) {
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
                dp[i] = 1;
            } else {
                dp[i] = 0;
            }
        }

        for (int i = t + 1; i <= (L + t - 1); i++) {
            minDP = 101;
            for (j = t; j >= s; j--) {
                minDP = min(minDP, dp[(i-j)%len]);
            }
            if (lastNIndex != m && nums[lastNIndex] == i) {
                lastNIndex++;
                dp[i%len] = minDP + 1;
            } else {
                dp[i%len] = minDP;
            }
        }

        minDP = 101;
        for (int i = L; i <= (L + t - 1); i++) {
            minDP = min(minDP, dp[i%len]);
        }

        printf("%d", minDP);
        
        free(dp);
    }
    
    
    free(nums);
    
    return 0;
}

C++ 解法, 执行用时: 3ms, 内存消耗: 400KB, 提交时间: 2022-05-19

#include<iostream>
#include<algorithm>
using namespace std;
int L, s, t, m, ans;
int a[110];  //石子的位置
int f[11000] = {0}; //f[x]表示青蛙跳到位置i最少踏的石子数
int stone[11000] = {0}; //stone[x]表示位置x是否是石子,0表示不是,1表示是
void solve() {
    int d = 0, k = s * t, x; //d表示累加平移量,k表示s和t的公倍数
    for (int i = 1; i <= m + 1; i++) {
        x = a[i] - d - a[i - 1]; //x表示第i个石子和第i-1个石子的距离
        if (x > k) d += x - k; //超过公倍数部分用作平移
        a[i] = a[i] - d;
        stone[a[i]] = 1; //标记平移后位置是石子
    }
    stone[a[m + 1]] = 0; //桥尾不是石子
    f[0] = 0;
    for (int i = 1; i <= a[m + 1] + t - 1;
            i++) { //考查桥上到桥尾的所有位置
        f[i] = 105;
        for (int j = s; j <= t;
                j++) //在i的前一个位置中找一个经历石子最少的
            if (i >= j) f[i] = min(f[i], f[i - j]);
        f[i] += stone[i]; //加上当前位置石子数
    }
    ans = 101;
    for (int i = a[m + 1]; i <= a[m + 1] + t - 1;
            i++) //在跳过桥后所有位置中找一个最小值
        ans = min(ans, f[i]);
    cout << ans << endl;
}
int main() {
    cin >> L >> s >> t >> m;
    ans = 0;
    a[0] = 0;
    a[m + 1] = L;
    for (int i = 1; i <= m; i++) cin >> a[i];
    sort(a + 1, a + m +
         1); //对桥中间石子位置排序,这上步必须要有
    if (s == t) { //这种情况只需考查石子是否是石子的倍数即可
        for (int i = 1; i <= m; i++)
            if (a[i] % s == 0)
                ans++;
        cout << ans << endl;
    } else
        solve();
    return 0;
}

C++ 解法, 执行用时: 3ms, 内存消耗: 412KB, 提交时间: 2022-05-04

#include<iostream>
#include<algorithm>  //排序和求最小值要用到此文件。
using namespace std;
int L,s,t,m,ans;
int a[110];  //保存石子位置 
int f[11000]={0};  //f[x]表示青蛙跳到位置i最少踏的石子数 
int stone[11000]={0}; //stone[x]表示位置x是否是石子,0表示不是,1表示是 
void solve()
{
    int d(0),k=s*t,x;  //d表示累加平移量,k表示s和t的公倍数 
    for (int i=1;i<=m+1;i++) 
    {
        x=a[i]-d-a[i-1];  //x表示第i个石子和第i-1个石子的距离 
        if (x>k) d+=x-k;  //超过公倍数部分用作平移 
        a[i]=a[i]-d;
        stone[a[i]]=1;  //标记平移后位置是石子 
    }
    stone[a[m+1]]=0; //桥尾不是石子 
    f[0]=0; 
    for (int i=1;i<=a[m+1]+t-1;i++)  //考查桥上到桥尾的所有位置 
    {
        f[i]=105;   
        for (int j=s;j<=t;j++) //在i的前一个位置中找一个经历石子最少的 
           if (i>=j) f[i]=min(f[i],f[i-j]);
        f[i]+=stone[i];  //加上当前位置石子数 
    }
    ans=101;
    for (int i=a[m+1];i<=a[m+1]+t-1;i++)  //在跳过桥后所有位置中找一个最小值 
        ans=min(ans,f[i]);
    cout<<ans<<endl;  
}
int main()
{
    cin>>L>>s>>t>>m;
    ans=0;
    a[0]=0; a[m+1]=L;
    for (int i=1;i<=m;i++) cin>>a[i];
    sort(a+1,a+m+1);  //对桥中间石子位置排序,这上步必须要有 
    if (s==t) {  //这种情况只需考查石子是否是石子的倍数即可 
        for (int i=1;i<=m;i++) 
            if (a[i]%s==0) 
                ans++;
        cout<<ans<<endl;   
    }
    else solve();
    return 0;
}

//http://www.cppblog.com/powerwater/articles/187391.html

C++ 解法, 执行用时: 3ms, 内存消耗: 420KB, 提交时间: 2022-06-17

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+9;
int h[N],s,t,n,l,a[N],dp[N];
int main()
{
	cin>>l>>s>>t>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	int len=s*t,dis=0;
	sort(a+1,a+n+1);
	if(s==t){
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			if(a[i]%t==0) ans++; 
		}
		cout<<ans;
		return 0;
	}
	for(int i=1;i<=n;i++){
		int l=a[i]-a[i-1];
		l=min(l,len);
		dis+=l;
		h[dis]=1;
	}
	for(int i=dis;i>=0;i--)
	{
		dp[i]=1e6;
		for(int j=s;j<=t;j++){
			dp[i]=min(dp[i],dp[j+i]+h[i]);
		}
	}
	cout<<dp[0];
}

上一题