P9271 [CEOI2013] Splot 题解
由于停车图是递归定义的,因此有必要为问题找到递归公式。考虑串联和并联组合后,需要调用其他需要计算的函数。对于给定停车图的所有子图
在上述所有函数中,可以使用特殊符号或数字(例如
此外,为了使公式更简单,我们定义一个对于子图
现在,需要为上述每个函数找到递归公式。这里详细解释了函数
- 如果该图只包含一个节点,则
\texttt{F,B,A} 的值始终为1 ,而\texttt{T,N} 的值要么为0 (如果这个节点没有车),要么为-\infty (这个节点停了车)。 - 假设我们串联
G_1,G_2 得到G ,并想要计算它的\texttt{F} 函数值。有两种可能性:至少有一辆汽车停在G_2 中,或者G_2 为空。在第一种情况下,必须要有一条路径让G_1 中的汽车全部在源点退出,因此,在此情况下最多可以停放的汽车数量为\texttt{T(}G_1\texttt{)+F(}G_2\texttt{)} 。在第二种情况下,最多可以停放的汽车数量是\texttt{F(}G_1\texttt{)+N(}G_2\texttt{)} ,因为G_2 必须为空,并且\texttt{F(}G_1\texttt{)} 给出了该情况下最佳的安排。 - 现在,假设
G 是并联组合的结果,并且我们要计算函数\texttt{F} 的值。并联组合更为复杂,因为我们还需要考虑新添加的源和终端是怎么样的。根据源和终端是否停了车,分为三种情况:- 源被占用,终点空闲,此时,
G_1 和G_2 都必须为空。\texttt{F(G)} 的值为1+\texttt{N(}G_1\texttt{)}+\texttt{N(}G_2\texttt{)} 。 - 源和终点都是空闲的:现在,来自
G_1 和G_2 的汽车可以直接向前退出,或者它们可以从其中一个的两侧退出,而另一个则具有从终端到汇点的路径。因此,\texttt{F(G)} 的值是\texttt{F(}G_1\texttt{)}+\texttt{F(}G_2\texttt{)},\texttt{B(}G_1\texttt{)}+\texttt{T(}G_2\texttt{)},\texttt{T(}G_1\texttt{)}+\texttt{B(}G_2\texttt{)} 中最大的一个。 - 源空闲,终点被占用:其中一个子图必须要保证停在终点的车辆可以退出到源,而另一个子图的所有汽车都需要向前退出。因此,
\texttt{F(G)} 的值为1+\texttt{T(}G_1\texttt{)}+\texttt{F(}G_2\texttt{)},1+\texttt{F(}G_1\texttt{)}+\texttt{T(}G_2\texttt{)} 中更大的一个。
- 源被占用,终点空闲,此时,
即使找到了递归计算函数的所有可能规则,由于需要考虑的情况数量巨大,实现(特别是重构最优排列时)仍然容易出错。一个寄巧可以极大地简化它的实现,那就是使用常量来定义上述递归规则,见下图。
上述表格将递归计算函数的规则编码为在 C++ 中的简单整数数组。例如,第二列中的 {FORWARD,0,1,THROUGH,FORWARD} 表示当
通过上述定义的常量,解决方案变得简单但仍然很长(不过还是好多了),需要实现一个解析器来将编码转换为类似树形结构的形式,然后需要递归遍历该树来计算每个函数。为了重构最优排列,我们需要记住用于获得相应函数最大值的规则索引。
下面是官方题解代码:
// CEOI 2013 - Task: Splot - Solution
// Author: Ante Derek
#include <cstdio>
#include <cassert>
using namespace std;
const int MAX = 1000000;
const int NEGINF = -(1<<28);
const int CFF_0102 = 542457;
enum FunctionType {FORWARD=0, BACKWARD=1, BOTH=2, THROUGH=3, NONE=4};
static const int S_RULE[][3] = {
{FORWARD, FORWARD, NONE},
{FORWARD, THROUGH, FORWARD},
{BACKWARD, NONE, BACKWARD},
{BACKWARD, BACKWARD, THROUGH},
{BOTH, FORWARD, BACKWARD},
{BOTH, BOTH, THROUGH},
{BOTH, THROUGH, BOTH},
{THROUGH, THROUGH, THROUGH},
{NONE, NONE, NONE},
{-1, -1, -1}
};
static const int P_RULE[][5] = {
{FORWARD, 1, 0, NONE, NONE},
{FORWARD, 0, 0, FORWARD, FORWARD},
{FORWARD, 0, 0, THROUGH, BOTH},
{FORWARD, 0, 0, BOTH, THROUGH},
{FORWARD, 0, 1, THROUGH, FORWARD},
{FORWARD, 0, 1, FORWARD, THROUGH},
{BACKWARD, 0, 1, NONE, NONE},
{BACKWARD, 0, 0, BACKWARD, BACKWARD},
{BACKWARD, 0, 0, THROUGH, BOTH},
{BACKWARD, 0, 0, BOTH, THROUGH},
{BACKWARD, 1, 0, THROUGH, BACKWARD},
{BACKWARD, 1, 0, BACKWARD, THROUGH},
{BOTH, 1, 1, NONE, NONE},
{BOTH, 1, 0, BACKWARD, BACKWARD},
{BOTH, 0, 1, FORWARD, FORWARD},
{BOTH, 0, 0, BOTH, BOTH},
{THROUGH, 0, 0, THROUGH, BOTH},
{THROUGH, 0, 0, BOTH, THROUGH},
{NONE, 0, 0, NONE, NONE},
{-1, -1, -1, -1}
};
class Splot {
enum SplotType {NODE, SERIES, PARALLEL} type;
int source;
int sink;
Splot *first;
Splot *second;
int result[5];
int how[5];
Splot(SplotType _type,
int _source=0,
int _sink=0,
Splot* a = NULL,
Splot* b = NULL):
type(_type),
source(_source),
sink(_sink),
first(a),
second(b) {
for (int i=0; i<5; i++)
how[i] = result[i] = -1;
}
public:
~Splot() {
if (first)
delete first;
if (second)
delete second;
}
static Splot* new_node(char c) {
return new Splot(NODE, c=='x', c=='x');
}
static Splot* new_series(Splot* a, Splot *b) {
return new Splot(SERIES, 0, 0, a, b);
}
static Splot* new_parallel(char s, char t, Splot* a, Splot* b) {
return new Splot(PARALLEL, s=='x', t=='x', a, b);
}
int node_result(int f) {
if (f == FORWARD || f == BACKWARD || f == BOTH)
return result[f] = 1;
return result[f] = source?NEGINF:0;
}
Splot* rec_node(int f) const {
if (f == FORWARD || f == BACKWARD || f == BOTH)
return new_node('x');
else
return new_node('o');
}
int series_result(int f) {
int &what = result[f];
what = NEGINF;
for (int l=0; S_RULE[l][0]!=-1; l++) {
if (S_RULE[l][0] != f)
continue ;
int left = first->get_result(S_RULE[l][1]);
int right = second->get_result(S_RULE[l][2]);
if (left != NEGINF && right != NEGINF && left+right > what) {
how[f] = l;
what = left+right;
}
}
return what;
}
Splot *rec_series(int f) const {
int l = how[f];
return new_series(first->reconstruct(S_RULE[l][1]),
second->reconstruct(S_RULE[l][2]));
}
int parallel_result(int f) {
int &what = result[f];
what = NEGINF;
for (int l=0; P_RULE[l][0]!=-1; l++) {
if (P_RULE[l][0] != f)
continue ;
int news = P_RULE[l][1];
int newt = P_RULE[l][2];
if ((source && !news) || (sink && !newt))
continue ;
int left = first->get_result(P_RULE[l][3]);
int right = second->get_result(P_RULE[l][4]);
if (left != NEGINF && right != NEGINF && left+right+news+newt > what) {
how[f] = l;
what = left+right+news+newt;
}
}
return what;
}
Splot *rec_parallel(int f) const {
int l = how[f];
return new_parallel(P_RULE[l][1]?'x':'o',
P_RULE[l][2]?'x':'o',
first->reconstruct(P_RULE[l][3]),
second->reconstruct(P_RULE[l][4]));
}
int get_result(int f) {
if (result[f] != -1)
return result[f];
if (type == NODE)
return node_result(f);
else if (type == SERIES)
return series_result(f);
else
return parallel_result(f);
}
Splot* reconstruct(int f) const {
if (type == NODE)
return rec_node(f);
else if (type == SERIES)
return rec_series(f);
else
return rec_parallel(f);
}
void print() const {
if (type == NODE)
printf("%c", source?'x':'o');
else if (type == SERIES) {
printf("S");
first->print();
second->print();
printf("#");
} else {
printf("P%c|", source?'x':'o');
first->print();
second->print();
printf("|%c#", sink?'x':'o');
}
}
};
class Parser {
const char *s;
int p;
Splot* do_parse() {
if (s[p] == 'o' || s[p] == 'x')
return Splot::new_node(s[p++]);
if (s[p] == 'S') {
p++;
Splot *first = do_parse();
Splot *second = do_parse();
p++;
return Splot::new_series(first, second);
}
assert(s[p] == 'P');
char sc = s[p+1];
p += 3;
Splot *first = do_parse();
Splot *second = do_parse();
char tc = s[p+1];
p += 3;
return Splot::new_parallel(sc, tc, first, second);
}
public:
Parser(const char *_s):
s(_s), p(0) {
}
Splot* parse() {
assert(p == 0);
Splot *r = do_parse();
assert(s[p] == 0 || s[p] == '\n');
return r;
}
};
int main() {
static char s[MAX+1];
scanf("%s", s);
Splot* splot = Parser(s).parse();
int best = splot->get_result(FORWARD);
printf("%d\n", best);
Splot *filled = splot->reconstruct(FORWARD);
filled->print();
printf("\n");
delete splot;
delete filled;
return 0;
}
最后附一份官方出题组的出题背景: