1. 程式人生 > 其它 >[SCOI2012]喵星人的入侵

[SCOI2012]喵星人的入侵

(思路大量參考 DP 帶師 UM 的部落格

一個能直接把人幹勸退的插頭 DP 。。

Description

給定一個 \(n * m\) 圖,有一個起點和終點和一些空地,你需要在空地上放置一些障礙和不超過 k 個炮臺(均不可經過),使得:

  1. 起點和終點連通,可以存在多條路徑;

  2. 每經過一個點(起終也算),答案加上該點八連通的炮臺數,最大化答案(注:多條路徑算答案最小的一條)。

\(\min(n,\ m) \leq 6,\ \max(n,\ m) \leq 20,\ 1\leq k \leq 15\)

Analysis

這個資料就很插頭 DP 把,那就不去想其他的了。

那既然是要路徑(不是迴路),那自然一行的狀態就直接 \(0\)

表示無插頭, \(1\) 表示左插頭, \(2\) 表示右插頭, \(3\) 表示獨立插頭。

但是這樣好像不太夠,因為考慮到八連通下的貢獻,需要知道附近幾個點的狀態,是路徑,是炮臺還是障礙。

所以需要一行狀態形如(粉色那一列):

對於打叉的點,記錄的就是這些格子是路徑,是炮臺還是障礙(其他還有四個格子因為那個點還沒遍歷到,不考慮)。

(這兩個都四進位制就行,注:之後插頭的叫狀態 \(1\) ,格子種類的叫狀態 \(2\)

還不夠,因為有炮臺的限制,所以還要加一個指炮臺數量(最多 \(15\) 個所以二進位制 \(4\) 位就夠了)。

Solution

根據 Analysis 裡說的,對於每個點,我們提取上一行狀態 \(1\)

的左格 \(pr\) 和上格 \(pd\) ,狀態 \(2\) 的左格 \(l\) ,左上格 \(ul\) ,上格 \(u\) ,右上格 \(ur\)

啊啊啊,** 大力分討!!(推薦畫圖食用)

  1. 如果左,上都是路徑但是都不是插頭,說明這個地方不能再有路徑接上去了,要麼放障礙,要麼放炮臺。(也就這種情況是和狀態 \(2\) 有關的)

  2. 這個點本來就是障礙點,那也就只有左,上均沒有插頭的時候能轉移。

  3. 起點或終點(可以形象化成一個獨立插頭)
    ①. 均沒有插頭
    直接新建獨立插頭。
    ②. 無插頭 + 左插頭(或左 + 無)
    刪除當前插頭,並且把對應的右插頭改成獨立插頭。
    ③. 無插頭 + 右插頭(或右 + 無)
    刪除當前插頭,並且把對應的左插頭改成獨立插頭。
    ④. 無插頭 + 獨立插頭(或獨立 + 無)
    刪除當前插頭,沒對應的插頭,所以沒了。

  4. 空地(插頭種類不定,跟著之前的走就行)
    ①. 均沒有插頭
    可以選擇放障礙,放炮臺,或者開一個新路徑。
    ②. 僅有一個有插頭
    跟著插頭走就行了。
    ③. 都是左插頭
    刪除這兩個插頭,並且把上格插頭對應的右插頭變成左插頭。
    ④. 都是右插頭
    刪除這兩個插頭,並且把左格插頭對應的左插頭變成右插頭。
    ⑤. 右插頭 + 左插頭
    刪除這兩個插頭,並且把對應的兩個插頭左右不變。
    ⑥. 左插頭 + 右插頭
    不合法,這樣就是閉合迴路。
    ⑦. 左插頭 + 獨立插頭(或獨立 + 左)
    刪除這兩個插頭,並且把對應的右插頭變成獨立插頭。
    ⑧. 右插頭 + 獨立插頭(或獨立 + 右)
    刪除這兩個插頭,並且把對應的左插頭變成獨立插頭。
    ⑨. 獨立插頭 + 獨立插頭
    直接刪除,沒什麼能伸展出去的。


接下來,來想想怎麼計算答案。

前面提到過,處理這個點的時候我們只知道周圍四個點的狀態。

那丟掉的四個點怎麼辦呢?在處理那四個點的時候自然就會算當前這個點了。正好,還不會算重。

因為相當於我們全部認定這個空地就是路徑,那麼他的貢獻就是周圍八個格子炮臺數。轉化一下也就是炮臺的貢獻就是周圍八個格子空地數。

所以如果是炮臺,就算已知的四個點裡面空地數;

所以如果是空地,就算已知的四個點裡面炮臺數。

那不是還有多條路徑算最小值的限制嗎?

那你把答案最大的那條留下,剩下的直接全部用障礙填滿,答案肯定不劣,況且這一定會在狀態中出現,所以可以覆蓋掉多條路徑的影響。

那全部答案取合法裡面的最大值就行了。

Code

Code

/*

*/
#include 
//#define int long long
#define BT1 ((l == 1) + (ul == 1) + (u == 1) + (ur == 1))
#define BT3 ((l == 3) + (ul == 3) + (u == 3) + (ur == 3))
using namespace std;
typedef long long ll;
const int N = 24, zs = 299987;
int n, m, K, a[N][N], p, las, fst[zs + 10], tot[2], ans, inc[N], pl[N];
struct BIT {
	int c0, c1, c2;
	bool operator == (const BIT &it) const {
		return (c0 == it.c0) && (c1 == it.c1) && (c2 == it.c2);
	}
};
struct mdzz {int nxt, val[2]; BIT bt[2];} e[(1 << 24) + 10];
inline int read() {
	int s = 0, w = 1;
	char ch = getchar();
	while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
	while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
	return s * w;
}
inline void insert(int c0, int c1, int c2, int num) {
	int u = ((((1ll * c0 << 16) | c1) << 4) | c2) % zs + 1;
	for (int i = fst[u]; i; i = e[i].nxt) {
		if (e[i].bt[p] == (BIT) {c0, c1, c2}) {
			e[i].val[p] = max(e[i].val[p], num);
			return ;
		}
	}
	e[++tot[p]].nxt = fst[u];
	e[tot[p]].bt[p] = (BIT) {c0, c1, c2};
	e[tot[p]].val[p] = num;
	fst[u] = tot[p];
}
int pr, pd, l, ul, u, ur, c0, c1, c2;
inline int RT(int c0, int j, int pm) {
	int tot = 1, x = c0, lm;
	while (2333) {
		lm = (x >> pl[j]) & 3;
		if (lm == 1) ++tot; else if (lm == 2) --tot;
		if (!tot) return c0 ^ (lm << pl[j]) ^ (pm << pl[j]);
		++j;
	}
	return 0;
}
inline int LT(int c0, int j, int pm) {
	int tot = 1, x = c0, lm;
	while (2333) {
		lm = (x >> pl[j]) & 3;
		if (lm == 2) ++tot; else if (lm == 1) --tot;
		if (!tot) return c0 ^ (lm << pl[j]) ^ (pm << pl[j]);
		--j;
	}
	return 0;
}
inline void Plugdp(int i, int j, BIT bit, int num) {
	//這裡的c0是已經把左和上去掉的殘缺狀態
	//這裡的c1是已經把左上去掉的殘缺狀態
	if ((!pr && l == 1) || (!pd && u == 1)) {
		if (pr || pd) return ;
		insert(c0, c1, c2, num);
		if (c2 < K && a[i][j] == 1) {
			insert(c0, c1 ^ (3 << pl[j]), c2 + 1, num + BT1);
		}
	}
	else if (a[i][j] == 2) {
		if (!pr && !pd) insert(c0, c1, c2, num);
	}
	else if (a[i][j] == 1) {
		if (!pr && !pd) {
			insert(c0, c1, c2, num);
			if (c2 < K) insert(c0, c1 ^ (3 << pl[j]), c2 + 1, num + BT1);
			insert(c0 ^ inc[j - 1] ^ (inc[j] << 1), c1 ^ inc[j], c2, num + BT3);
		}
		else if (!pr || !pd) {
			insert(c0 ^ ((pd | pr) << pl[j - 1]), c1 ^ inc[j], c2, num + BT3);
			insert(c0 ^ ((pd | pr) << pl[j]), c1 ^ inc[j], c2, num + BT3);
		}
		else if (pr == 1 && pd == 1) insert(RT(c0, j - 1, 1), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 2 && pd == 2) insert(LT(c0, j - 1, 2), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 2 && pd == 1) insert(c0, c1 ^ inc[j], c2, num + BT3);
		else if (pr == 1 && pd == 2) ;
		else if (pr == 1 && pd == 3) insert(RT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 3 && pd == 1) insert(RT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 2 && pd == 3) insert(LT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 3 && pd == 2) insert(LT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 3 && pd == 3) insert(c0, c1 ^ inc[j], c2, num + BT3);
	}
	else if (a[i][j] == 3 || a[i][j] == 4) {
		if (!pr && !pd) {
			insert(c0 ^ (3 << pl[j - 1]), c1 ^ inc[j], c2, num + BT3);
			insert(c0 ^ (3 << pl[j]), c1 ^ inc[j], c2, num + BT3);
		}
		else if (!pr && pd == 1) insert(RT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 1 && !pd) insert(RT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (!pr && pd == 2) insert(LT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 2 && !pd) insert(LT(c0, j - 1, 3), c1 ^ inc[j], c2, num + BT3);
		else if (pr == 3 && !pd) insert(c0, c1 ^ inc[j], c2, num + BT3);
		else if (!pr && pd == 3) insert(c0, c1 ^ inc[j], c2, num + BT3);
	}
}
inline void plug() {
	tot[p] = 1;
	for (int i = 1; i <= n; ++i) {
		for (int j = 1, x; j <= tot[p]; ++j) {
			e[j].bt[p].c0 <<= 2;
			x = e[j].bt[p].c1;
			(e[j].bt[p].c1 ^= ((x >> pl[m + 1]) & 3) << pl[m + 1]) <<= 2;
		}
		for (int j = 1; j <= m; ++j) {
			memset(fst, 0, sizeof(fst));
			swap(las, p); tot[p] = 0;
			for (int k = 1, num; k <= tot[las]; ++k) {
				BIT bit = e[k].bt[las]; num = e[k].val[las];
				if (bit.c0 >= inc[m + 1]) continue;
				c0 = bit.c0; c1 = bit.c1; c2 = bit.c2;
				pr = (c0 >> pl[j - 1]) & 3; pd = (c0 >> pl[j]) & 3;
				l = (c1 >> pl[j - 1]) & 3; ul = (c1 >> pl[j]) & 3;
				u = (c1 >> pl[j + 1]) & 3; ur = (c1 >> pl[j + 2]) & 3;
				c0 ^= (pr << pl[j - 1]) ^ (pd << pl[j]); c1 ^= (ul << pl[j]);
				Plugdp(i, j, bit, num);
			}
		}
	}
}
inline int pdd(char ch) {
	if (ch == '.') return 1;
	if (ch == '#') return 2;
	if (ch == 'S') return 3;
	if (ch == 'T') return 4;
	if (ch == 'X') return 5;
	return 0;
}
int main() {
	n = read(); m = read(); K = read();
	inc[0] = 1; las = 1;
	for (int i = 1; i <= 14; ++i) {
		inc[i] = inc[i - 1] << 2, pl[i] = i << 1;
	}
	if (n >= m) {
		char ch;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= m; ++j) {
				ch = getchar();
				while (!(a[i][j] = pdd(ch))) ch = getchar();
			}
		}
	}
	else {
		swap(n, m);
		char ch;
		for (int j = 1; j <= m; ++j) {
			for (int i = 1; i <= n; ++i) {
				ch = getchar();
				while (!(a[i][j] = pdd(ch))) ch = getchar();
			}
		}
	}
	plug();
	for (int i = 1; i <= tot[p]; ++i) {
		if (!e[i].bt[p].c0) ans = max(ans, e[i].val[p]);
	}
	printf("%d\n", ans);
	return 0;
}