zoukankan      html  css  js  c++  java
  • luogu P1052 过河

    先看一下题面。


    背景

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

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

    分析

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

      那自然考虑搜索啦~ 

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

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

      那么尝试去搜搜石子吧?

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

    那么就只能DP了啊。

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

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

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

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

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

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

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

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

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

      吼啊,离散化准备¿

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

      真?dp+离散化?

      可以的!


    方法一:DP+离散化

      首先轻松写出DP方程
            min( ifla] )(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
    ",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
    ",ans);
    		return 0;
    	}
    }

    怎么可能只有一种做法?

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

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

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

      然后我们继续向后看,可以发现 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愉快!!!!!!!!!!!

  • 相关阅读:
    input文本框输入限制(正则表达式)
    SQL Server通用型分页存储过程
    简单易学的数据图表
    HTML中input文本框只读不可编辑的方法
    SQL添加外键约束的方式
    +1 也要睁着眼
    博客园的自定义皮肤
    网站收集整理
    jQuery extend方法介绍
    HTML5本地存储
  • 原文地址:https://www.cnblogs.com/qxyzili--24/p/11177578.html
Copyright © 2011-2022 走看看