NOIP 2005过河(DP+路径压缩)

30分做法:

dp[i]表示青蛙跳到位置i经过的最小石子数,从dp[i-j]转移过里,s<=j<=t。初始状态:dp[0]=0;

最后统计答案是从[n,n+t)区间中找出最小dp值。(n+t青蛙跳不到,所以开区间)

 

#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int l,s,t,m;
int a[107];
int b[11007];
int dp[11007];
int main()
{
 scanf("%d%d%d%d",&l,&s,&t,&m);
 for(int i=1;i<=m;i++)
 {
   scanf("%d",a+i);
   b[a[i]]=1;
 }
 memset(dp,63,sizeof(dp));
 dp[0]=0;
 for(int i=s;i<l+t;i++)
 {
  for(int j=s;j<=t;j++)
  {
    if(i-j>=0) dp[i]=min(dp[i],dp[i-j])+b[i];
  }
 }
 int ans=99999;
 for(int i=l;i<l+t;i++)
 {
   ans=min(ans,dp[i]);
 }
 printf("%d\n",ans);
 return 0;    
}

 

满分做法:

观察数据范围l<=1e9,这么大数组肯定存不下来,所以考虑路径压缩。因为s,t是1-10之间的数,所以无论青蛙怎么跳,他都能从当前位置i跳到i+2520*X的位置,

因为2520是1-10的最小公倍数,他的因子包含1-10所有数。对相邻两个石子的距离进行路径压缩,对2520取模(因为中间空余的长度大于2520,青蛙可以直接跳过,不会对答案有影响),

这样可以大大缩短桥的长度。重新更新石子的位置,最后桥长就等于最后石子的位置,其他操作和30分一样.

 

#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
const int maxm=1e6+7;
int l,s,t,m;
int a[117];
int b[maxm];
int d[117];
int dp[maxm];
int main()
{
 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);//题目没说升序排列
 for(int i=1;i<=m;i++)//路径压缩 
 {
   d[i]=(a[i]-a[i-1])%2520;//2520 1-10的最小公倍数 
 }
 for(int i=1;i<=m;i++)
 {
  a[i]=a[i-1]+d[i];
  b[a[i]]=1; 
 }
 l=a[m];//更新新长度 
 memset(dp,63,sizeof(dp));
 dp[0]=0;
 for(int i=s;i<l+t;i++)
 {
  for(int j=s;j<=t;j++)
  {
    if(i-j>=0) dp[i]=min(dp[i],dp[i-j])+b[i];
  }
 }
 int ans=99999;
 for(int i=l;i<l+t;i++)
 {
   ans=min(ans,dp[i]);
 }
 printf("%d\n",ans);
 return 0;    
}

 

 

 

 

       

posted @ 2019-10-11 15:13  lihan123  阅读(306)  评论(0编辑  收藏  举报