luogu P1052 过河

先看一下题面。


背景

  这道2005年的提高组时至今日仍然评级在提高+/省选-,由此可见这题确实不简单。

  虽然都说是很简单的DP了,但是坑点依然很多。

分析

不过我开始并没有看出来是DP。

  那自然考虑搜索啦~

  首先考虑搜索在渡河的途中,踩到的最小石子数量。

  然而1..10^9长度的桥。就算是O(n)的算法也不能在一秒内出解。

  那么尝试去搜搜石子吧?

  搜一个石子就需要考虑来源和去路,导致算法更困难了。。。

那么就只能DP了啊。

  以石子分阶段的一维动规,时间复杂度是O(n^2)。

  最多也只有100×100的时间。但是这样分状态就十分复杂。

  因为石头的分布是没有任何规律,而且会有后效性。

  ???????????????

  这题怎么做啊?????????

  然后我就去买了杯可乐,回来写了个爆搜加了点剪枝,搞了一组数据。

  然后蒟蒻我就开始面向数据编程研究数据,然后我就发现了一个神奇的东西¿

  虽然桥的长度......

  然而,石子的数量......【日常看数据范围不看全系列

  吼啊,离散化准备¿

  ????????????????

  真?dp+离散化?

  可以的!


方法一:DP+离散化

  首先轻松写出DP方程
        f [ i ] = min( f [ i−j ] + flag [ i ] )(s<=j<=t)

  我们可以发现当两点间的距离d大于t时,一定可以由d%t跳过来。

  所以最多只需要t+d%t种距离的状态,就可以表示这两个石子之间的任意距离关系。

  这样就把题目中的10^9压缩成了2*t*m最多不超过2000,然后就可以放心大胆地用DP了

代码奉上【同机房大佬友情赞助 这么丑代码怎么可能是我写的

#include<bits/stdc++.h>

using namespace std ;

long long l,s,t,m,a[20010],dis[20010],ans,flag[20010];
long long dp[20010];

int main(){
	scanf("%lld",&l);
	scanf("%lld%lld%lld",&s,&t,&m);
	if(s == t){
		long long what;
		for(int i=1;i<=m;i++) {
			scanf("%lld",&what);
			ans += ((what % s) == 0);
		}
		printf("%lld\n",ans);
		return 0;
	}
	else {
		long long tp = 0;
		for(int i=1;i<=m;i++){
			scanf("%lld",&a[i]);
		}
		sort(a+1,a+1+m);
		for(int i=1;i<=m;i++){
			dis[i] = min(a[i] - a[i-1] , (long long)90);
			tp += dis[i];
			flag[tp] = 1;
		}
		dis[m+1] = min((long long)100,l - a[m]);
		tp += dis[m+1];
		for(int i=1;i<=tp+9;i++) {
			dp[i] = 0x6fffffff;
			for(int j=s;j<=t;j++) {
				if(i>=j) dp[i] = min(dp[i-j] + flag[i] , dp[i]);
			}
		}
		ans = 0x6fffffff;
		for(int i=tp;i<=tp+9;i++) {
			ans = min (ans, dp[i]);
		}
		printf("%lld\n",ans);
		return 0;
	}
}


怎么可能只有一种做法?

  我们来仔细想想,画个图手玩一下。

  假如 s < t ,青蛙一定会跳到一个特殊的位置,s*t。

    即当s < t时, s和t一定会重合(当距离为lcm(s,t)即s,t的最小公倍数时) 。

  然后我们继续向后看,可以发现 s*t 以后的每个点都可以到达,。

  所以我们只需将每两个石头超过 s*t 的距离缩成 s*t 就可以了

顺便带来一波数学证明。

  

图片来源是luogu中的 Panda_hu 的题解。

方法二:路径压缩

  代码奉上

  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

long long l;
int s,t,M;
int m[101],sum[10001];
int q[10000001];

int cmp(int a,int b) {
    return a<b;
}

int main()
{
    scanf("%lld%d%d%d",&l,&s,&t,&M);
    for(int i=1;i<=M;i++)
        scanf("%d",&m[i]);
    sort(m+1,m+M+1,cmp);
    int temp=0;
    for(int i=1;i<=M;i++) {
        if(m[i]-m[i-1]<=t*s)
            temp+=m[i]-m[i-1];
        else
            temp+=(m[i]-m[i-1])%(s*t)+s*t;
        q[temp]=1;
    }
    memset(sum,0x3f,sizeof sum);
    sum[0]=0;
    for(int i=1;i<=temp+t;i++) {
        for(int j=s;j<=t;j++) {
            if(i-j<0)
                continue;
            sum[i]=min(sum[i],sum[i-j]+q[i]);
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=temp+t-s;i<=temp+t;i++)
        ans=min(ans,sum[i]);
    cout<<ans<<endl;
    return 0;
}

  

AC愉快!!!!!!!!!!!

原文地址:https://www.cnblogs.com/qxyzili--24/p/11177578.html

时间: 2024-11-08 10:19:25

luogu P1052 过河的相关文章

Luogu P1052 过河 DP

复习复习DP...都忘了QAQ... 好了这道题我其实是看题解才会的... 方程 f[i]=min(f[i-j]+v[i]) v[i]表示i是不是石头 s<=j<=t 路径压缩引用一下证明From [email protected]_Hu #include<cstdio> #include<iostream> #define R register int #include<algorithm> #define R register int const int

P1052 过河(状态压缩)

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

洛谷P1052 过河 动态规划

洛谷P1052 过河通过观察可以发现 这个点很稀疏 dp 有很长一段距离都是没有用的,那么我们可以采用离散化的思想 把这个距离压缩,但同时还要保证 对答案没有影响 如果 s==t 这时候我们需要特判 只要判断 pos[ i ] % s == 0 就可以知道是否踩到石子 然后因为 最多青蛙一次只跳了 10 假如 s == 9 t == 10 如果两个石子间的距离大于100 我们每次也可以 一步步的慢慢调整 ,其实总共只有10个状态,%10==1 %10==2 %10==3 然后我们就可以把距离 >

noip 2005 luogu cogs P1052 过河 WD

111. [NOIP2005] 过河 ★★★   输入文件:river.in   输出文件:river.out   简单对比 时间限制:1 s   内存限制:128 MB [问题描述] 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整 数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点: 0 , 1 ,--, L (其中 L 是桥的长度).坐标为 0 的点表示桥的起点,坐标为 L 的点表示桥

洛谷 P1052 过河

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

luogu P1002 过河卒

题目描述 棋盘上A点有一个过河卒,需要走到目标B点.卒行走的规则:可以向下.或者向右.同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点.因此称之为“马拦过河卒”. 棋盘用坐标表示,A点(0, 0).B点(n, m)(n, m为不超过20的整数),同样马的位置坐标是需要给出的. 现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步. 输入输出格式 输入格式: 一行四个数据,分别表示B点坐标和马的坐标. 输出格式: 一

luogu P1809 过河问题_NOI导刊2011提高(01)

题目描述 有一个大晴天,Oliver与同学们一共N人出游,他们走到一条河的东岸边,想要过河到西岸.而东岸边有一条小船. 船太小了,一次只能乘坐两人.每个人都有一个渡河时间T,船划到对岸的时间等于船上渡河时间较长的人所用时间. 现在已知N个人的渡河时间T,Oliver想要你告诉他,他们最少要花费多少时间,才能使所有人都过河. 注意,只有船在东岸(西岸)的人才能坐上船划到对岸. 输入输出格式 输入格式: 输入文件第一行为人数N,以下有N行,每行一个数. 第i+1行的数为第i个人的渡河时间. 输出格式

P1052 过河[DP]

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

P1052 过河

需要在一个简答的动态规划上加一点东西 给一些状态数非常多但是重复状态非常多的动规提供了一种可能的解决方法. #include<iostream> #include<algorithm> #define N 1000005 using namespace std; int a[N],d[N],stone[N]; int f[N]; int main(){ int l,s,t,m; cin>>l>>s>>t>>m; for(int i=1