1. 程式人生 > 其它 >一雙木棋 chess

一雙木棋 chess

題意

link

\(n \times m, (n, m\leq 10)\) 的矩陣,每次可以在左上都有棋子的地方下,奇數次下在 \((i, j)\) 的價值是 \(a_{i, j}\),偶數次是 \(-b_{i, j}\)

奇數次時想讓價值和最大,偶數次時想讓價值和最小。

問最終結果。

輪廓線dp,狀壓dp

狀態

如果第 \(i\) 行放了 \(c_i\) 個,那麼它一定左到右連續的。

並且從第 \(1\) 行到第 \(n\) 行,\(c_i\) 是遞減的。

狀態就可以用輪廓線唯一表示。

\(0\) 表示 \(m\) 條橫向的線,用 \(1\) 表示 \(n\) 條豎向的線。

\(f[S]\)

表示棋盤狀態 \(S\) 到全部下完的價值。

初始和最終狀態

由於轉移是從最終倒回來的。

初始狀態 \(f[111\cdots 0000\cdots] = 0\), 表示全部下完離全部下完的價值和是 0。

其它全部設為無窮大或無窮小。

最終狀態 \(f[0000\cdots 111\cdots]\) 表示初始狀態離全部下完的價值。

轉移

能發現選一格,實際上的輪廓線就是把 \(1\) 和 下一位的 \(0\) 交換。

選的位置可以用輪廓線狀態算出來,設為 \((x, y)\)

所以就有:
1.偶數次時

\[f[S] = \max_{S_i = 1, S_{i + 1} = 0} \{f[S \oplus (3 << i)] + a[x][y] \} \]

2.奇數次時

\[f[S] = \min_{S_i = 1, S_{i + 1} = 0} \{f[S \oplus (3 << i)] - b[x][y] \} \]
分析

狀態是 \(O(2^{n + m})\) 的,轉移是 \(O(n + m)\)

最終是 \(O(2^{n + m}(n + m))\)

程式碼

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int MAXN = 15;
const int INF = 0x3f3f3f3f;
//const int mod = 1000000007;
int mod; 
const double eps = 1e-9; 

template <typename T>
void Read(T &x) {
	x = 0; T f = 1; char a = getchar();
	for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
	for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
	x *= f;
}

inline int add(const int &a, const int &b) { 
	static int c;
	c = a + b;
	if (c >= mod) c -= mod;
	if (c < 0) c += mod;
	return c;   
} 
inline int mul(const int &a, const int &b) {
	return 1ll * a * b % mod; 
}
int qpow(int a, int b) {
	int sum(1);
	while(b) {
		if (b & 1) sum = mul(sum, a);
		a = mul(a, a);
		b >>= 1;
	}
	return sum; 
}

int n, m;
int a[MAXN][MAXN], b[MAXN][MAXN];

int f[1 << 21]; 
bool vis[1 << 21];
int dfs(int S, int N) {
	if (vis[S]) return f[S];
	vis[S] = 1;
	f[S] = N ? - INF : INF;
	int x = n, y = 0;
	for (int i = 0; i < n + m - 1; i ++) {
		if (S >> i & 1) x --;
		else y ++; 
		if ((S >> i & 3) != 1) continue;
		int to = S ^ (3 << i);
		if (N) f[S] = max(f[S], dfs(to, N ^ 1) + a[x + 1][y + 1]);
		else f[S] = min(f[S], dfs(to, N ^ 1) - b[x + 1][y + 1]); 
	}
	return f[S];
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++)
			cin >> a[i][j];
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++)
			cin >> b[i][j];
	memset(f, 0x3f, sizeof(f)); 
	f[((1 << n) - 1) << m] = 0; 
	vis[((1 << n) - 1) << m] = 1; 
	cout << dfs((1 << n) - 1, 1);
	return 0;
}