某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 }