Luogu P1052 过河【DP】

在这里插入图片描述
这道题不是一道状压DP。
当然,如果您想用我的方法写题,
建议您先去看看Luogu P3951 小凯的疑惑【数论】,这道题是前置知识。

为什么呢?
首先看这题的数据范围在这里插入图片描述
明显直接DP会炸。
我们在小凯的疑惑里可得,最大不能到的价值为 a b − a − b ab-a-b abab,后面的都能得到。
那我们不妨照这题的思路,将它带入到这题里。
当奇数和偶数都被枚举到时,后面所有情况都会被枚举到,
那么我们考虑最大能枚举到奇数和偶数的位置,一番推理 可得,这个位置是 T ∗ ( T − 1 ) T*(T-1) T(T1),这样我们就可以压缩空间和运算次数

 for(int i=1; i<=m; i++)
     {
    
    
     	y[i]=min(a[i]-a[i-1],90);  //10*(10-1)=90
     	l+=y[i];  //重新统计长度
     	flag[l]=1;  //标记石头的位置
     }

然后我们开始DP。
f [ i ] f[i] f[i] 表示当前第 i i i 个点走过了 x x x 个石头。
可得动态转移方程为:
f [ i ] = min ⁡ ( f [ i ] , f [ i − j ] + f l a g [ i ] ) ; f[i]=\min(f[i],f[i-j]+flag[i]); f[i]=min(f[i],f[ij]+flag[i]);
flag存的是当前点的有没有石头。

代码

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

int a[1001],y[1001],flag[9000001],f[9000001];
int l,s,t,m,js;
 
int main()
{
    
    
    cin>>l>>s>>t>>m;
    for(int i=1; i<=m; i++)
     {
    
    
     	scanf("%d",&a[i]);
     	if(a[i]%s==0)
     	  js++;
     }
    if(s==t)
     {
    
    
     	cout<<js;
     	return 0;
     }
    sort(a+1,a+1+m);
    y[m+1]=min(l-a[m],90);
    l=0;
    for(int i=1; i<=m; i++)
     {
    
    
     	y[i]=min(a[i]-a[i-1],90);
     	l+=y[i];
     	flag[l]=1;
     }
    l+=y[m+1];
    for(int i=1; i<=l+9; i++)  //可能会转移到 l 后面的状态,所以要到 l+9
     {
    
    
     	f[i]=100100101;
     	for(int j=s; j<=t; j++)
     	 if(i>=j)
            f[i]=min(f[i],f[i-j]+flag[i]);
     }
    int ans=100010010;
    for(int i=l; i<=l+9; i++)
       ans=min(ans,f[i]);
    cout<<ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Jackma_mayichao/article/details/108132713