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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/84565366

【思路要点】

  • 求出 SSTT 的路径长度 mm 以及路径上的每一个点 pathipath_i
  • 记路径上 STS\rightarrow T 的方向为向前, TST\rightarrow S 的方向为向后。
  • 注意到若在 aa 处的障碍移动到了 bb 处, bb 处的障碍移动到了 cc 处,那么我们也可以认为是 aa 处的障碍穿过了 bb 处移动到了 cc 处,因此,我们可以认为障碍可以穿过障碍移动,但不能停留在障碍上。
  • 不难发现,有意义的障碍移动分为两种:
    (1)(1) 、从路径上某处 pathfrompath_{from} 先向前或向后移动至路径上某个点 pathxpath_x ,再移入其的子树中,停留在节点 yy 上。
    (2)(2) 、从路径上某处 pathfrompath_{from} 先向前移动至路径上某个点 pathxpath_x ,等棋子拐入 pathy(y<x)path_y(y<x) 的子树后向后移动至路径上某个点 pathz(z<y)path_z(z<y)
  • 需要说明的是, (2)(2) 中描述的障碍不会移出 SSTT 的路径,否则我们可以直接将其视为 (1)(1) 中描述的障碍。 (1)(1) 中描述的障碍可以在放下棋子之前完成移动。
  • (1)(1) 中描述的障碍移动距离为 xfrom+dist(y,pathx)|x-from|+dist(y,path_x)
    (2)(2) 中描述的障碍移动距离为 2xfromz2x-from-z
  • (1)(1) 中描述的障碍移动细分为先向前移动的,称为 (1)+(1)+ ,和先向后移动的,称为 (1)(1)- ,这样可以展开表达式中的绝对值。
  • 注意到 (1)+(1)+(2)(2) 的移动距离中均含有 from-from 一项,考虑将它们一并处理,因此我们需要在状态中记录尚未匹配的 fromfrom 端的个数。同时,由于 (2)(2) 类障碍还存在一端 zz ,并且这样的 zz 端必须在经过 yy ,即棋子拐入的节点后才可以匹配 fromfrom 端,我们还需要记录尚未匹配的,尚未经过 yyzz 端的个数,以及已经经过 yyzz 端的个数。
  • 接下来我们来设计 dpdp ,记状态 (t,i,j,k)(t,i,j,k) 表示完成了路径上前 tt 个点的决策,有 ii 个尚未匹配的 fromfrom 端, jj 个尚未匹配的 zz 端尚未经过 yykk 个尚未匹配的 zz 端已经经过了 yy 。这里的 ii 可以是负数,表示需要之后的 i-ifromfrom 端来走入之前的子树。
  • 转移时首先我们需要枚举有多少 fromfrom 端将走入 tt 的子树,显然我们会优先选取取深度最小的目标节点。
  • 枚举 outout ,即棋子是否在 pathtpath_t 处拐入子树,若拐入,需满足 tt 处存在子树,步数将增加 22 ,并且所有尚未经过 yyzz 端在此之后会转化为已经经过 yyzz 端。
  • 枚举 lftlft ,即是否用 pathtpath_t 处的障碍匹配一个已经经过 yyzz 端,若是,需满足 pathtpath_t 处存在障碍,并且存在可以匹配的 zz 端,步数将增加 tt ,可以匹配的 zz 端数量将减少 11
  • 枚举 revrev ,即是否将 tt 处作为 (2)(2) 类障碍的 xx 点匹配一对 from,zfrom,z 端,若是,需满足 lft=falselft=false ,并且存在可以匹配的 zz 端,步数将增加 2t2tfromfrom 端和可以匹配的 zz 端数量将减少 11
  • 枚举 extext ,即是否将 tt 处的空位用作一个新的 (2)(2) 类障碍的 zz 端,若是,步数将增加 t-t ,尚未经过 yyzz 端将增加 11
  • pathtpath_t 处的障碍没有用作匹配已经经过 yyzz 端,则需要将其计入 fromfrom 端的个数中。
  • 所有 pathtpath_t 的子树大小和为 O(N)O(N) ,因此时间复杂度为 O(NM3)O(NM^3) ,需要滚动数组优化空间。

【代码】

#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;
}
阅读更多

没有更多推荐了,返回首页