P1941 飞扬的小鸟[NOIP2014提高组]

前50分显然是非常水的。甚至能用搜索过。

#include
using namespace std;

#define pb push_back
#define fi first
#define se second
#define ll long long
#define pq priority_queue
#define mp make_pair
#define pii pair
#define mod 998244353
#define debug(x) cerr<<#x<<"="< q;
int n,m,k;
int l[maxn],r[maxn];
int x[maxn],y[maxn];
bool pipe[maxn];

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (int i=0;i=r[i]) continue;
        if (pipe[i]) cnt++;
        ans=max(ans,cnt);
        if (f>=ans2) continue;
        if (j-y[i]>0) q.push_front(build(i+1,j-y[i],f,cnt));
        q.push_back(build(i+1,min(m,j+x[i]),f+1,cnt));
        if (j+x[i]>=m) continue;
        q.push_back(build(i+1,min(m,j+2*x[i]),f+2,cnt));
        if (j+2*x[i]>=m) continue;
        q.push_back(build(i+1,min(m,j+3*x[i]),f+3,cnt));
    }
    if (ans2==1e9) cout<<0<

这道题显然就是一道dp。状态也非常显然:

f[i][j]表示考虑到横坐标为i的位置高度为j的最小跳跃次数。转移分两种情况:

  • 不跳,直接从前一个位置掉下来:f[i][j]=f[i-1][j+y[i-1]]
  • 跳:枚举所有比j低且与j高度差为x[i-1] k倍(k为正整数)的高度h:f[i][j]=min(f[i][j],f[i-1][h]+k)
  • 特别注意当j=m时需要特判

显然,第一种情况的转移是O(1)的,但第二种情况转移为O(m),那么总的复杂度为O(nm^2),只能得到70分。因此我们需要对第二种情况的转移进行优化。令g[i][j]为第二种情况的最小花费。

对于每一步转移时取min的值分为两个部分:f数组和k。可以发现g[i][j]在转移时用到的f数组只比g[i][j-x[i-1]]多一个f[i-1][j]+1,其他部分完全相同,而对于转移时用到的每一个相同的f数组,g[i][j]在转移时后面对应的k比g[i][j-x[i-1]]多1。因此

g[i][j]=min(g[i][j-x[i-1]+1,f[i-1][k]+1)

这样我们就将转移的复杂度降到了O(1)。

还是一定要注意j=m是的特判。

代码如下:

#include
using namespace std;

#define pb push_back
#define fi first
#define se second
#define ll long long
#define pq priority_queue
#define mp make_pair
#define pii pair
#define mod 998244353
#define debug(x) cerr<<#x<<"="<0) g[j]=min(f[i-1][j-x[i-1]],g[j-x[i-1]])+1;
                else g[j]=INF;
                if (j==m) {
                    for (int k=j;k>=max(0,j-x[i-1]);k--) {
                        g[j]=min(g[j],min(g[k],f[i-1][k])+1);
                    }
                }
            }
            for (int j=l[i]+1;j0) {
            break;
        }
        else if (pipe[i]) cnt++;
    }
    if (ans==INF) cout<<0<

 

你可能感兴趣的:(算法——动态规划)