• 某DP题目3


      题意:

        一根数轴上有n只怪物,第i个怪物所在的位置为ai,另有m个特殊点,第i个特殊点所在的位置为bi。你可以对怪物进行移动,若两怪物相邻,那么你不能把他们分开,移动时要看作一个整体。你可以选择向左或向右移动,直到撞到怪物,移动的次数不限制。现在要求最大数量的呆在特殊点上的怪物。 n <= 100000,m <= 2000

      分析:

        做这题的时候真的非常烦躁,卡在描述状态很久,静下心来思考才能更好地领悟。

        由于最终答案与特殊点密切相关,且特殊点是固定不变的,我们不妨以特殊点为基础作转移。接下来我们就可以分情况来考虑了:假设第i只怪兽在特殊点j的左边,那么就需要pos[j]-pos[i]只怪兽填充,即第i+1~i+pos[j]-pos[i]向左移动;反之亦然。

        然后我就思考状态,发现无论怎么想,转移都涉及到左移和右移,会出现状态重叠的情况。

        那么就要把左移和右移分开,使转移的时候不出现状态重叠的情况。我们令F[i]为前i只怪兽能到达的特殊点的最大数量,而且还带有限制,对于转移怪兽i的时候,怪兽i只能选择不动或者向左移动。那不是说怪兽可以左右移吗?是的,只是怪兽i的向右移是在怪兽i+1~n的转移中,将左右移分开了。这样F[i]所得到的方案必满足所有的怪兽小于等于第i只怪兽的位置pos[i],避免了状态的重叠,这在后面的转移当中至关重要。

        那么要如何转移呢?

          其实在上面的思考中,已经可以得到答案了。

          对于F[i]的转移,只有两种:不动或者向左移动。转移是基于特殊点的,你的转移都是为了去占满更多的特殊点,分类讨论如何占满。

          1、不动。枚举位置比怪兽i小的特殊点j,若要占据特殊点j,那么就需要i-(pos[i]-pos[j])~i-1的怪兽右移,转移方程即为:F[i] = max{F[i-(pos[i]-pos[j])-1]+1}。当然还需要注意怪兽连成一块的情况。

          2、向左移动。枚举位置特殊点j,以及预处理出位置最大不超过特殊点j的怪兽p。若要占据特殊点j,那么i-p需大于等于pos[j]-pos[p],转移方程即为:F[i] = max{F[p]+1}。这里如果出现连成一块的情况的话,就需要判断第i只怪兽是否在块的最右端,否则,又会出现状态重叠的情况。

     1 #include <cstdio>
     2 #include <cstdlib>
     3 #include <cstring>
     4 #include <string>
     5 #include <algorithm>
     6 #include <iostream>
     7 
     8 using namespace std;
     9 
    10 const int maxn = 100005;
    11 const int maxm = 2005;
    12 int n, m, a[maxn], b[maxm];
    13 int belong[maxn], lef_block[maxn], rig_block[maxn], Bcnt;
    14 int p[maxm], f[maxn];
    15 
    16 void in()
    17 {
    18     scanf("%d %d", &n, &m);
    19     for (int i = 1; i <= n; ++i)
    20         scanf("%d", &a[i]);
    21     for (int i = 1; i <= m; ++i)
    22         scanf("%d", &b[i]);
    23 }
    24 
    25 void prepare()
    26 {
    27     sort(a+1, a+n+1);
    28     sort(b+1, b+m+1);
    29     Bcnt = 0;
    30     for (int i = 1; i <= n; ++i)
    31     {
    32         if (a[i] != a[i-1]+1 || i == 1)
    33         {
    34             belong[i] = ++Bcnt;
    35             lef_block[Bcnt] = i;
    36         }
    37         else
    38             belong[i] = Bcnt;
    39         if (a[i+1] != a[i]+1)
    40             rig_block[Bcnt] = i;
    41     }
    42     for (int i = 1; i <= m; ++i)
    43     {
    44         p[i] = p[i-1];
    45         while (a[p[i]+1] <= b[i])
    46             p[i] ++;
    47     }
    48 }
    49 
    50 void dp()
    51 {
    52     f[0] = 0;
    53     for (int i = 1; i <= n; ++i)
    54     {
    55         f[i] = f[i-1];
    56         for (int j = 1; j <= m; ++j){
    57             if (b[j] <= a[i])
    58             {
    59                 //part 1 : i don't move, others rig-move
    60                 if (i-(a[i]-b[j]) >= 1)
    61                 {
    62                     int t = lef_block[belong[i-(a[i]-b[j])]];
    63                     f[i] = max(f[t-1]+1, f[i]);
    64                 }
    65                 //part 2 : i lef-move
    66                 if (b[j]-a[p[j]] <= i-p[j] && rig_block[belong[i]] == i)
    67                 {
    68                     f[i] = max(f[p[j]]+1, f[i]);
    69                 }
    70             }
    71             else
    72                 break ;
    73         }
    74     }
    75     printf("%d
    ", f[n]);
    76 }
    77 
    78 void work()
    79 {
    80     prepare();
    81     dp();
    82 }
    83 
    84 int main()
    85 {
    86     freopen("a.in", "r", stdin);
    87     freopen("a.out", "w", stdout);
    88     in();
    89     work();
    90     return 0;
    91 }
  • 相关阅读:
    filter过滤器(转载)
    匿名函数 lambda
    偏函数(转载)
    python中的多重继承和Mixin(转载)
    __slot__
    virtual hust 2013.6.21 NEFU 挑战编程----数论 E
    virtual hust 2013.6.21 NEFU 挑战编程----数论 D
    virtual hust 2013.6.21 NEFU 挑战编程----数论 C
    virtual hust 2013.6.21 NEFU 挑战编程----数论 B
    virtual hust 2013.6.21 NEFU 挑战编程----数论 A
  • 原文地址:https://www.cnblogs.com/-ZZB-/p/6427229.html
Copyright © 2020-2023  润新知