某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\n", 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 }

 

posted @ 2017-02-22 07:41  Splay  阅读(271)  评论(0编辑  收藏  举报