【省内训练2018-11-25】Chess

【思路要点】

  • 求出 S S S T T T 的路径长度 m m m 以及路径上的每一个点 p a t h i path_i pathi
  • 记路径上 S → T S\rightarrow T ST 的方向为向前, T → S T\rightarrow S TS 的方向为向后。
  • 注意到若在 a a a 处的障碍移动到了 b b b 处, b b b 处的障碍移动到了 c c c 处,那么我们也可以认为是 a a a 处的障碍穿过了 b b b 处移动到了 c c c 处,因此,我们可以认为障碍可以穿过障碍移动,但不能停留在障碍上。
  • 不难发现,有意义的障碍移动分为两种:
    ( 1 ) (1) (1) 、从路径上某处 p a t h f r o m path_{from} pathfrom 先向前或向后移动至路径上某个点 p a t h x path_x pathx ,再移入其的子树中,停留在节点 y y y 上。
    ( 2 ) (2) (2) 、从路径上某处 p a t h f r o m path_{from} pathfrom 先向前移动至路径上某个点 p a t h x path_x pathx ,等棋子拐入 p a t h y ( y &lt; x ) path_y(y&lt;x) pathy(y<x) 的子树后向后移动至路径上某个点 p a t h z ( z &lt; y ) path_z(z&lt;y) pathz(z<y)
  • 需要说明的是, ( 2 ) (2) (2) 中描述的障碍不会移出 S S S T T T 的路径,否则我们可以直接将其视为 ( 1 ) (1) (1) 中描述的障碍。 ( 1 ) (1) (1) 中描述的障碍可以在放下棋子之前完成移动。
  • ( 1 ) (1) (1) 中描述的障碍移动距离为 ∣ x − f r o m ∣ + d i s t ( y , p a t h x ) |x-from|+dist(y,path_x) xfrom+dist(y,pathx)
    ( 2 ) (2) (2) 中描述的障碍移动距离为 2 x − f r o m − z 2x-from-z 2xfromz
  • ( 1 ) (1) (1) 中描述的障碍移动细分为先向前移动的,称为 ( 1 ) + (1)+ (1)+ ,和先向后移动的,称为 ( 1 ) − (1)- (1) ,这样可以展开表达式中的绝对值。
  • 注意到 ( 1 ) + (1)+ (1)+ ( 2 ) (2) (2) 的移动距离中均含有 − f r o m -from from 一项,考虑将它们一并处理,因此我们需要在状态中记录尚未匹配的 f r o m from from 端的个数。同时,由于 ( 2 ) (2) (2) 类障碍还存在一端 z z z ,并且这样的 z z z 端必须在经过 y y y ,即棋子拐入的节点后才可以匹配 f r o m from from 端,我们还需要记录尚未匹配的,尚未经过 y y y z z z 端的个数,以及已经经过 y y y z z z 端的个数。
  • 接下来我们来设计 d p dp dp ,记状态 ( t , i , j , k ) (t,i,j,k) (t,i,j,k) 表示完成了路径上前 t t t 个点的决策,有 i i i 个尚未匹配的 f r o m from from 端, j j j 个尚未匹配的 z z z 端尚未经过 y y y k k k 个尚未匹配的 z z z 端已经经过了 y y y 。这里的 i i i 可以是负数,表示需要之后的 − i -i i f r o m from from 端来走入之前的子树。
  • 转移时首先我们需要枚举有多少 f r o m from from 端将走入 t t t 的子树,显然我们会优先选取取深度最小的目标节点。
  • 枚举 o u t out out ,即棋子是否在 p a t h t path_t patht 处拐入子树,若拐入,需满足 t t t 处存在子树,步数将增加 2 2 2 ,并且所有尚未经过 y y y z z z 端在此之后会转化为已经经过 y y y z z z 端。
  • 枚举 l f t lft lft ,即是否用 p a t h t path_t patht 处的障碍匹配一个已经经过 y y y z z z 端,若是,需满足 p a t h t path_t patht 处存在障碍,并且存在可以匹配的 z z z 端,步数将增加 t t t ,可以匹配的 z z z 端数量将减少 1 1 1
  • 枚举 r e v rev rev ,即是否将 t t t 处作为 ( 2 ) (2) (2) 类障碍的 x x x 点匹配一对 f r o m , z from,z from,z 端,若是,需满足 l f t = f a l s e lft=false lft=false ,并且存在可以匹配的 z z z 端,步数将增加 2 t 2t 2t f r o m from from 端和可以匹配的 z z z 端数量将减少 1 1 1
  • 枚举 e x t ext ext ,即是否将 t t t 处的空位用作一个新的 ( 2 ) (2) (2) 类障碍的 z z z 端,若是,步数将增加 − t -t t ,尚未经过 y y y z z z 端将增加 1 1 1
  • p a t h t path_t patht 处的障碍没有用作匹配已经经过 y y y z z z 端,则需要将其计入 f r o m from from 端的个数中。
  • 所有 p a t h t path_t patht 的子树大小和为 O ( N ) O(N) O(N) ,因此时间复杂度为 O ( N M 3 ) O(NM^3) O(NM3) ,需要滚动数组优化空间。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 305;
const int MAXM = 105;
const int INF = 1e9;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
char mp[MAXN];
int s, t; vector <int> a[MAXN];
int cnt, rnk[MAXN], path[MAXN];
int timer, dfn[MAXN], pos[MAXN];
int n, belong[MAXN], dist[MAXN];
int dp[2][MAXM * 2][MAXM][MAXM];
bool online[MAXN], outside[MAXN];
bool dfs(int pos, int fa) {
	bool ans = pos == s;
	for (auto x : a[pos])
		if (x != fa) ans |= dfs(x, pos);
	if (ans) {
		online[pos] = true;
		rnk[pos] = ++cnt;
		path[cnt] = pos;
	}
	return ans;
}
void work(int cur, int fa, int home, int dis) {
	dist[cur] = dis;
	belong[cur] = home;
	if (dis >= 2) {
		dfn[cur] = ++timer;
		pos[timer] = cur;
	}
	for (auto x : a[cur])
		if (x != fa) work(x, cur, home, dis + 1);
}
int main() {
	read(n), read(s), read(t);
	scanf("\n%s", mp + 1);
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	assert(dfs(t, 0));
	for (int i = 1; i <= cnt; i++) {
		dfn[path[i]] = ++timer;
		pos[timer] = path[i];
		for (auto x : a[path[i]])
			if (!online[x]) {
				outside[path[i]] = true;
				work(x, path[i], path[i], 1);
			}
	}
	for (int i = 0; i <= cnt * 2; i++)
	for (int j = 0; j <= cnt; j++)
	for (int k = 0; j + k <= cnt; k++)
		dp[1][i][j][k] = INF;
	dp[1][cnt][0][0] = 0;
	for (int t = 1, now = 1, dest = 0; t <= timer; t++, swap(now, dest)) {
		for (int i = 0; i <= cnt * 2; i++)
		for (int j = 0; j <= cnt; j++)
		for (int k = 0; j + k <= cnt; k++)
			dp[dest][i][j][k] = INF;
		for (int i = 0; i <= cnt * 2; i++)
		for (int j = 0; j <= cnt; j++)
		for (int k = 0; j + k <= cnt; k++) {
			// i : carry, j : right, not revsered, k : right, reverseed.
			int tmp = dp[now][i][j][k];
			if (tmp >= n * n * n + 2333) continue;
			int now = pos[t];
			if (!online[now]) {
				chkmin(dp[dest][i][j][k], tmp);
				auto sign = [&] (int x) {return (x <= cnt) ? -1 : 1; };
				if (i >= 1) chkmin(dp[dest][i - 1][j][k], tmp + dist[now] + sign(i) * rnk[belong[now]]);
				continue;
			}
			for (int out = 0; out <= outside[now]; out++)
			for (int lft = 0; lft <= (mp[now] == 'X') && lft <= k; lft++)
			for (int rev = 0; rev + lft <= 1 && rev <= k && rev <= i; rev++)
			for (int ext = 0; ext <= 1; ext++) {
				int steps = tmp, di = i, dj = j, dk = k;
				if (out) dk += dj, dj = 0, steps += 2;
				if (lft) dk--, steps += rnk[now];
				if (rev) dk--, di--, steps += rnk[now] * 2;
				if (ext) dj++, steps -= rnk[now];
				auto sign = [&] (int x) {return (x < cnt) ? 1 : -1; };
				if ((mp[now] == 'X') - lft) chkmin(dp[dest][di + 1][dj][dk], steps + sign(di) * rnk[now]);
				else chkmin(dp[dest][di][dj][dk], steps);
			}
		}
	}
	int ans = cnt + dp[(timer + 1) & 1][cnt][0][0];
	if (ans >= n * n * n + 2333) writeln(-1);
	else writeln(ans); 
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值