【NOI2006】 千年虫解题报告
题目描述
千年虫是远古时代的生物,时隔几千万年,千年虫早已从地球上销声匿迹,
人们对其知之甚少。考古生物学家最近开始对其有了兴趣,
因为一批珍贵的千年虫化石被发现,这些化石保留了千年虫近乎完整的形态。
理论科学家们根据这些化石归纳出了千年虫的一般形态特征模型,
并且据此判定出千年虫就是蜈蚣的祖先!但科学家J发现了实际与理论
的一些出入,他仔细的研究了上百个千年虫化石,发现其中大部分千年虫的
形态都不完全符合理论模型,这到底是什么因素造成的呢?理论科学家K敏
锐的指出,千年虫的形态保存在化石中很有可能发生各种变化,即便最细
微的变化也能导致它不符合模型。于是,摆在科学家面前的新问题诞生了:
判断一个化石中的千年虫与理论模型的差距有多大?
具体来说,就是根据一个千年虫化石的形态A,找到一个符合理论模型的形态B,使 得B是最有可能在形成化石时变成形态A。 理论学家提出的“千年虫形态特征模型”如下(如上图所示):躯体由头、尾、躯干、足四大部分构成。
1.头,尾用一对平行线段表示。称平行于头、尾的方向为x方向;垂直于x的方向为y方向;
2.在头尾之间有两条互不相交的折线段相连,他们与头、尾两条线段一起围成的区域称为躯干,两条折线段都满足以下条件:拐角均为钝角或者平角,且包含奇数条线段,从上往下数的奇数条垂直于x方向。
3.每条折线段从上往下数的第偶数条线段的躯干的另一侧长出一条足,即一个上、下底平行于x方向的梯形或矩形,且其中远离躯干一侧的边垂直于x方向。
注意:足不能退化成三角形(即底边的长度均大于零),躯干两侧足的数目可以不一样。
(如下图,左边有4条足,右边有5条足) 可见,x-y直角坐标系内,躯干和所有足组成的实心区域的边界均平行或垂直于坐标轴。
为了方便,我们假设所有这些边界的长度均为正整数。因此可以认为每个千年虫的躯体都由一些单位方格拼成。每个单位方格都由坐标(x,y)唯一确定。
设头尾之间的距离为n,则我们可以用2×n个整数来描述一条千年虫B(如右图):将B沿平行x轴方向剖分成n条宽度为1的横条,每个横条最左边一格的x坐标设为Li,最右一格的的x坐标设为Ri。则(n,L1,L2,..,Ln,R1,R2,..Rn)就确定了一条千年虫。
由于岁月的侵蚀,在实际发现的化石中,千年虫的形状并不满足上面理论模型的规则,一些格子中的躯体已经被某些矿物质溶解腐蚀了。地质、物理、生物学家共同研究得出:
1、腐蚀是以格子为单位的,只能一整格被腐蚀;
2、腐蚀是分步进行的,每一步只有一格被腐蚀;
3、如果去掉一个格子后躯体不连通了,那么这个格子当前不会被腐蚀;
4、如果一个格子的左边邻格和右边邻格都还没被腐蚀,那么这个格子当前不会被腐蚀;
5、与头相邻的格子不能全部被腐蚀,与尾相邻的格子不能全部被腐蚀;倘若满足上面五条,我们仍然可以用(n,L’1,L’2,..,L’n,R’1,R’2,..R’n)来描述一个化石里头的千年虫的形态。其中L’i≤R’i。
例如下图: 现在你的任务是,输入一个化石里的千年虫的描述A,找一个满足理论模型的千年虫的描述B,使得B可以通过腐蚀过程得以变为A,且由B转化为A的代价(须被腐蚀的格子数)最少。输出此最小代价。
DP——O(n^2)
1 /*
2 *Problem: NOI2006 千年虫
3 *Author : Chen Yang
4 *Time : 2012.5.31 8:00 pm
5 *State : 40分
6 *Memo : dp、维数优化
7 */
8 #include <cstdio>
9 #include <cstdlib>
10 #include <cstring>
11 #include <string>
12 #include <algorithm>
13 using namespace std;
14 const int maxn = 100010, inf = 1000000000;
15 int n, ans, l[maxn], now[maxn], f[2][maxn][2], best0[maxn];
16
17 void work()
18 {
19 memset(f, 0x7f, sizeof f);
20 memset(best0, 0x7f, sizeof best0);
21 int Max = 0;
22 for (int i=1; i<n+1; ++i) Max = max(Max, now[i]); Max++;
23 for (int i=0; i<=Max-now[1]; ++i) f[1][now[1]+i][0] = i;
24 int ths = 1, pst = 0;
25 for (int i=2; i<n+1; ++i)
26 {
27 ths ^= 1; pst ^= 1;
28 if (i>2) for (int j=now[i-2]; j<Max+1; ++j) f[ths][j][0] = f[ths][j][1] = inf;
29 int best1 = inf;
30 for (int j=now[i-1]; j<now[i]; ++j) best1 = min(best1, f[pst][j][0]);
31 for (int j=Max; j>now[i]; --j) best0[j] = min(f[pst][j][1],best0[j+1]);
32 for (int j=now[i]; j<Max+1; ++j)
33 {
34 if (j-1>=now[i-1]) best1 = min(best1, f[pst][j-1][0]);
35 f[ths][j][1] = min(f[pst][j][1], best1);
36 f[ths][j][0] = min(f[pst][j][0], best0[j+1]);
37 f[ths][j][1] += j-now[i]; f[ths][j][0] += j-now[i];
38 }
39 }
40 int Min = inf;
41 for (int i=now[n]; i<Max+1; ++i) Min = min(Min, f[ths][i][0]);
42 ans += Min;
43 }
44
45 int main()
46 {
47 freopen("worm.in", "r", stdin);
48 freopen("worm.out", "w", stdout);
49 scanf("%d", &n);
50 for (int i=1; i<n+1; ++i) scanf("%d%d", l+i, now+i);
51 work();
52 for (int i=1; i<n+1; ++i) now[i] = maxn-l[i];
53 work();
54 printf("%d", ans);
55 return 0;
56 }
DP——O(n)
1 /*
2 *Problem: NOI2006 千年虫
3 *Author : Chen Yang
4 *Time : 2012.5.31 10:00 pm
5 *State : Solved
6 *Memo : DP、维数优化、范围优化
7 */
8 #include <cstdio>
9 #include <cstdlib>
10 #include <cstring>
11 #include <string>
12 #include <algorithm>
13 using namespace std;
14 const int maxn = 400010, inf = 100000000;
15 int n, ans, l[maxn], now[maxn], p[2], f[2][20][2], q[2][maxn];
16
17 inline int getint()
18 {
19 int res = 0; char tmp;
20 while (!isdigit(tmp = getchar()));
21 do res = (res << 3) + (res << 1) + tmp - '0';
22 while (isdigit(tmp = getchar()));
23 return res;
24 }
25
26 void work()
27 {
28 memset(f, 0x7, sizeof f); p[1] = 0;
29 for (int j=1; j<4; ++j)
30 for (int k=now[j]; k<now[j]+3; ++k) if (k>=now[1]) q[1][++p[1]] = k;
31 for (int i=1; i<=p[1]; ++i) f[1][i][0] = q[1][i] - now[1];
32 int ths = 1, pst = 0;
33 for (int i=2; i<n+1; ++i)
34 {
35 ths ^= 1; pst ^= 1, p[ths] = 0;
36 int a = i-2<1? 1:i-2, b = n<i+2? n:i+2;
37 for (int j=a; j<b+1; ++j)
38 for (int k=now[j]; k<now[j]+3; ++k) if (k>=now[i]) q[ths][++p[ths]] = k;
39 for (int j=1; j<p[ths]+1; ++j)
40 {
41 f[ths][j][1] = inf, f[ths][j][0] = inf;
42 for (int k=1; k<p[pst]+1; ++k)
43 {
44 //printf("%d %d\n", f[pst][k][0],f[pst][k][1]);
45 if (q[pst][k]>q[ths][j]) f[ths][j][0] = min(f[ths][j][0], f[pst][k][1]); else
46 if (q[pst][k]<q[ths][j]) f[ths][j][1] = min(f[ths][j][1], f[pst][k][0]); else
47 f[ths][j][0] = min(f[ths][j][0], f[pst][k][0]),
48 f[ths][j][1] = min(f[ths][j][1], f[pst][k][1]);
49 }
50 f[ths][j][0] += q[ths][j]-now[i], f[ths][j][1] += q[ths][j]-now[i];
51 //printf("%d %d\n", f[ths][j][0],f[ths][j][1]);
52 }
53 }
54 int Min = inf;
55 for (int i=1; i<p[ths]+1; ++i) Min = min(Min, f[ths][i][0]);
56 ans += Min;
57 }
58
59 int main()
60 {
61 freopen("worm.in", "r", stdin);
62 freopen("worm.out", "w", stdout);
63 n = getint();
64 for (int i=1; i<n+1; ++i) l[i] = getint(), now[i] = getint();
65 work();
66 for (int i=1; i<n+1; ++i) now[i] = maxn-l[i];
67 work();
68 printf("%d", ans);
69 return 0;
70 }